diff --git a/.github/ISSUE_TEMPLATE/localization-suggestion.md b/.github/ISSUE_TEMPLATE/localization-suggestion.md index 89242dc038103..a1e2cce2e3bfc 100644 --- a/.github/ISSUE_TEMPLATE/localization-suggestion.md +++ b/.github/ISSUE_TEMPLATE/localization-suggestion.md @@ -40,7 +40,8 @@ https://github.com/dotnet/roslyn --- +## Roslyn Team Instructions A Roslyn team member will file a bug through https://aka.ms/ceLocBug which the translation team will consider. -Internal Tracking Issue: {Update with tracking issue URL.} \ No newline at end of file +Internal Tracking Issue: {Update with tracking issue URL.} diff --git a/.vscode/launch.json b/.vscode/launch.json index 623dd170dfb88..c207821918995 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/artifacts/bin/BuildValidator/Debug/net6.0/BuildValidator.dll", + "program": "${workspaceFolder}/artifacts/bin/BuildValidator/Debug/net7.0/BuildValidator.dll", "args": [ "--assembliesPath", "./artifacts/obj/csc/Debug/net6.0", "--referencesPath", "./artifacts/bin", @@ -29,8 +29,8 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/artifacts/bin/RunTests/Debug/net6.0/RunTests.dll", - "args": ["--tfm", "net6.0", "--sequential", "--html"], + "program": "${workspaceFolder}/artifacts/bin/RunTests/Debug/net7.0/RunTests.dll", + "args": ["--tfm", "net7.0", "--sequential", "--html"], "cwd": "${workspaceFolder}/artifacts/bin/RunTests", "stopAtEntry": false, "console": "internalConsole" @@ -41,7 +41,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/artifacts/bin/PrepareTests/Debug/net6.0/PrepareTests.dll", + "program": "${workspaceFolder}/artifacts/bin/PrepareTests/Debug/net7.0/PrepareTests.dll", "args": [ "--source", "${workspaceFolder}", "--destination", "${workspaceFolder}/artifacts/testPayload" diff --git a/Directory.Build.targets b/Directory.Build.targets index 2cad2d7b596e2..1eaf13d9d3612 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,20 +1,6 @@ - - - - 6.0.0 - - - 6.0.0 - - - - - - false - - + diff --git a/Roslyn.sln b/Roslyn.sln index 7b3342cc3ad33..6acd166b5be20 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -511,6 +511,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Net.Compilers.Too EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests", "src\Compilers\CSharp\Test\EndToEnd\Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests.csproj", "{C247414A-8946-4BAB-BE1F-C82B90C63EF6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageServerProtocol.Framework", "src\Features\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.csproj", "{D2589BCE-4F2E-4113-B7E7-37392C0C5492}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageServerProtocol.Framework.Example", "src\Features\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework.Example\Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj", "{BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageServerProtocol.Framework.UnitTests", "src\Features\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework.UnitTests\Microsoft.CommonLanguageServerProtocol.Framework.UnitTests.csproj", "{58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1241,6 +1247,18 @@ Global {C247414A-8946-4BAB-BE1F-C82B90C63EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU {C247414A-8946-4BAB-BE1F-C82B90C63EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {C247414A-8946-4BAB-BE1F-C82B90C63EF6}.Release|Any CPU.Build.0 = Release|Any CPU + {D2589BCE-4F2E-4113-B7E7-37392C0C5492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2589BCE-4F2E-4113-B7E7-37392C0C5492}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2589BCE-4F2E-4113-B7E7-37392C0C5492}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2589BCE-4F2E-4113-B7E7-37392C0C5492}.Release|Any CPU.Build.0 = Release|Any CPU + {BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9}.Release|Any CPU.Build.0 = Release|Any CPU + {58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1473,6 +1491,9 @@ Global {6131713D-DFB4-49B5-8010-50071FED3E85} = {C52D8057-43AF-40E6-A01B-6CDBB7301985} {A9A8ADE5-F123-4109-9FA4-4B92F1657043} = {C52D8057-43AF-40E6-A01B-6CDBB7301985} {C247414A-8946-4BAB-BE1F-C82B90C63EF6} = {32A48625-F0AD-419D-828B-A50BDABA38EA} + {D2589BCE-4F2E-4113-B7E7-37392C0C5492} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} + {BD9539EB-AA5E-4E67-AC7F-97D7CBC4D0C9} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} + {58AD1B2C-6FFC-47CB-838A-54D0CA2BF0C8} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 7d98e6e0d9a82..8083649094752 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -40,6 +40,8 @@ variables: - name: _DotNetValidationArtifactsCategory value: .NETCoreValidation - group: DotNet-Roslyn-SDLValidation-Params + - name: Codeql.Enabled + value: true​ # To retrieve OptProf data we need to authenticate to the VS drop storage. # Get access token with $dn-bot-devdiv-drop-rw-code-rw and dn-bot-dnceng-build-rw-code-rw from DotNet-VSTS-Infra-Access diff --git a/azure-pipelines-pr-validation.yml b/azure-pipelines-pr-validation.yml index f1390a9c58547..e53b7f62f4397 100644 --- a/azure-pipelines-pr-validation.yml +++ b/azure-pipelines-pr-validation.yml @@ -146,6 +146,15 @@ stages: /p:IgnoreIbcMergeErrors=true condition: succeeded() + - template: eng\common\templates\steps\generate-sbom.yml + + - task: PowerShell@2 + displayName: Publish Assets + inputs: + filePath: 'eng\publish-assets.ps1' + arguments: '-configuration $(BuildConfiguration) -branchName "$(SourceBranchName)"' + condition: succeeded() + # Publish OptProf generated JSON files as a build artifact. This allows for easy inspection from # a build execution. - task: PublishBuildArtifacts@1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2e478f2487f2c..617c0c7add041 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -38,8 +38,8 @@ stages: testArtifactName: Transport_Artifacts_Windows_Debug configuration: Debug queueName: windows.vs2022preview.amd64.open - restoreArguments: -msbuildEngine dotnet /p:UsingToolPdbConverter=false /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false - buildArguments: -msbuildEngine dotnet /p:UsingToolPdbConverter=false /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false /p:PublishReadyToRun=false + restoreArguments: -msbuildEngine dotnet /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false + buildArguments: -msbuildEngine dotnet /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false /p:PublishReadyToRun=false - stage: Windows_Release_Build dependsOn: [] @@ -50,8 +50,8 @@ stages: testArtifactName: Transport_Artifacts_Windows_Release configuration: Release queueName: windows.vs2022preview.amd64.open - restoreArguments: -msbuildEngine dotnet /p:UsingToolPdbConverter=false /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false - buildArguments: -msbuildEngine dotnet /p:UsingToolPdbConverter=false /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false /p:PublishReadyToRun=false + restoreArguments: -msbuildEngine dotnet /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false + buildArguments: -msbuildEngine dotnet /p:UsingToolVSSDK=false /p:GenerateSatelliteAssemblies=false /p:PublishReadyToRun=false - stage: Unix_Build dependsOn: [] diff --git a/docs/Breaking API Changes.md b/docs/Breaking API Changes.md index cfdf98d31c450..994d2b6dd8d3a 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 `null` `Workspace` and `DocumentId`. diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 6d58164825c87..ee5e6ee0d1d3d 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -102,6 +102,33 @@ Possible workarounds are: Also, implicit conversions between `IntPtr`/`UIntPtr` and other numeric types are treated as standard conversions on such platforms. This can affect overload resolution in some cases. +These changes could cause a behavioral change if the user code was depending on overflow exceptions in an +unchecked context, or if it was not expecting overflow exceptions in a checked context. An analyzer was +[added in 7.0](https://github.com/dotnet/runtime/issues/74022) to help detect such behavioral changes +and take appropriate action. The analyzer will produce diagnostics on potential behavioral changes, which default +to info severity but can be upgraded to warnings via [editorconfig](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#severity-level). + +## Addition of System.UIntPtr and System.Int32 + +***Introduced in .NET SDK 7.0.100, Visual Studio 2022 version 17.3.*** + +When the platform supports __numeric__ `IntPtr` and `UIntPtr` types (as indicated by the presence of +`System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr`), the operator `+(UIntPtr, int)` defined in `System.UIntPtr` +can no longer be used. +Instead, adding expressions of types `System.UIntPtr` and a `System.Int32` results in an error: + +```csharp +UIntPtr M(UIntPtr x, int y) +{ + return x + y; // error: Operator '+' is ambiguous on operands of type 'nuint' and 'int' +} +``` + +Possible workarounds are: + +1. Use the `UIntPtr.Add(UIntPtr, int)` method: `UIntPtr.Add(x, y)` +2. Apply an unchecked cast to type `nuint` on the second operand: `x + unchecked((nuint)y)` + ## Nameof operator in attribute on method or local function ***Introduced in .NET SDK 6.0.400, Visual Studio 2022 version 17.3.*** diff --git a/docs/compilers/Design/Unexpected Conditions.md b/docs/compilers/Design/Unexpected Conditions.md index 1d6a203fd1375..e53ce20d9b4dc 100644 --- a/docs/compilers/Design/Unexpected Conditions.md +++ b/docs/compilers/Design/Unexpected Conditions.md @@ -2,7 +2,7 @@ These are [guidelines](https://www.youtube.com/watch?v=6GMkuPiIZ2k) for writing - Use `Debug.Assert(Condition)` to document invariants in the code. Although these dissolve away into nothing in non-Debug builds, as an open-source project we have little control over whether our customers are running our code with Debug enabled. Therefore such assertions should not consume excessive execution time. We may consider having such assertions run in production builds in the future. If we find they are too expensive we may create work items to improve their performance. - If you write a switch statement that is intended to be exhaustive of the possibilities, add a default branch with `throw ExceptionUtilities.UnexpectedValue(switchExpression);`. -- Similarly, if you have a sequence of `if-then-else if` statements that is intended to be exhaustive of the possibilities, add a final "impossible" else branch with `throw ExceptionUtilities.Unreachable;` or, if is convenient to provide interesting, easy to obtain data for the diagnostic, use `ExceptionUtilities.UnexpectedValue`. Do this also for other situations where the code reaches a point that is "impossible". +- Similarly, if you have a sequence of `if-then-else if` statements that is intended to be exhaustive of the possibilities, add a final "impossible" else branch with `throw ExceptionUtilities.Unreachable();` or, if is convenient to provide interesting, easy to obtain data for the diagnostic, use `ExceptionUtilities.UnexpectedValue`. Do this also for other situations where the code reaches a point that is "impossible". - Validation of preconditions in public APIs should be done with plain code. Report a diagnostic if one exists for the situation (e.g. a syntax error), or throw an appropriate exception such as `ArgumentNullException` or `ArgumentException`. - If you run into some other weird error case that would be fatal, throw `InvalidOperationException` with interesting, straightforward to get, data in the message. These should be rare and should be accompanied by a comment explaining why an `Assert` is not sufficient. diff --git a/docs/ide/external-access.md b/docs/ide/external-access.md index e09926a4a1de6..23fa9d46cf31b 100644 --- a/docs/ide/external-access.md +++ b/docs/ide/external-access.md @@ -1,5 +1,13 @@ # External Access Policies +## API Tracking + +We track the APIs that we've exposed with a roslyn analyzer, which will fail the build if APIs are removed. Unlike our public APIs, we are allowed to have breaking changes here, but these will often cause insertion failures. Any removal of an existing API (whether the API is fully removed, or the parameters/return changed) needs to have sign off from the current infrastructure rotation, and will potentially require a test insertion before merging to ensure VS isn't broken, or coordinated insertion with the affected partner team. + +Because "shipping" for EA projects occurs every time a change is checked in, we don't bother updating the `InternalAPI.Shipped.txt` file for these projects. + +Every EA project has an `Internal` namespace that is considered Roslyn implementation details. For example, the OmniSharp namespace is `Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Internal`. We do not track the APIs under these namespaces, and it's on the dependent projects to not reference anything from these namespaces. Not every EA project actually uses this namespace. If an EA project that doesn't currently use their `Internal` namespace wants to start, modify `/src/Tools/ExternalAccess/.editorconfig` to set the `dotnet_public_api_analyzer.skip_namespaces` key to that namespace for the affected files. Multiple namespaces can be included by using a `,` separator. + ## OmniSharp When a change needs to be made to an API in the ExternalAccess.OmniSharp or ExternalAccess.OmniSharp.CSharp packages, ping @333fred, @JoeRobich, @filipw, or @david-driscoll as a heads up. Breaking changes are allowed, but please wait for acknowledgement and followup questions to ensure that we don't completely break OmniSharp scenarios. diff --git a/docs/wiki/NuGet-packages.md b/docs/wiki/NuGet-packages.md index 5a70764af7896..3039916c9625c 100644 --- a/docs/wiki/NuGet-packages.md +++ b/docs/wiki/NuGet-packages.md @@ -39,6 +39,9 @@ Below are the versions of the language available in the NuGet packages. Remember - Version `3.10` includes C# 9.0 (Visual Studio 2019 version 16.10, .NET 5) - Version `3.11` includes C# 9.0 (Visual Studio 2019 version 16.11, .NET 5) - Version `4.0` includes C# 10.0 (Visual Studio 2022 version 17.0, .NET 6) +- Version `4.1` includes C# 10.0 (Visual Studio 2022 version 17.1, .NET 6) +- Version `4.2` includes C# 10.0 (Visual Studio 2022 version 17.2, .NET 6) +- Version `4.3.1` includes C# 10.0 (Visual Studio 2022 version 17.3, .NET 6) See the [history of C# language features](https://github.com/dotnet/csharplang/blob/main/Language-Version-History.md) for more details. diff --git a/eng/Versions.props b/eng/Versions.props index 16e299603bd7d..5fe6ee87804c8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -6,23 +6,23 @@ --> 4 - 4 + 5 0 - 4 + 1 $(MajorVersion).$(MinorVersion).$(PatchVersion) $(MajorVersion).$(MinorVersion).0.0 - 4.3.0-1.final + 4.4.0-2.final - 3.3.4-beta1.22204.1 + 3.3.4-beta1.22504.1 7.0.0-preview1.22218.1 - 1.1.2-beta1.22462.1 - 0.1.145-beta + 1.1.2-beta1.22512.1 + 0.1.149-beta 4.3.0-1.final 17.4.203-preview @@ -53,6 +53,7 @@ 1.2.4 1.2.4 1.2.4 + 1.3.0 1.2.4 0.13.0 0.13.0 @@ -61,7 +62,8 @@ 2.14.1 7.1.0.6543 1.0.7 - 1.4.1 + 1.5.5 + 6.0.0 6.0.0 + 6.0.0 6.0.0 4.5.0 4.5.4 - 2.0.7 + 2.1.0 1.1.0.14 1.1.87 17.0.1056-Dev17PIAs-g9dffd635 @@ -297,7 +300,7 @@ true - true + false true true true @@ -305,7 +308,6 @@ true true true - true - - - - \ No newline at end of file diff --git a/eng/targets/Imports.BeforeArcade.targets b/eng/targets/Imports.BeforeArcade.targets new file mode 100644 index 0000000000000..aeb820317be69 --- /dev/null +++ b/eng/targets/Imports.BeforeArcade.targets @@ -0,0 +1,31 @@ + + + + + + + true + false + $(AssemblyName).Symbols + $(AssemblyName).$(PlatformTarget).Symbols + + + + + + 6.0.0 + + + 6.0.0 + + + + + + false + + + diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets index 0320ff6eb4dd0..e4264602c2caf 100644 --- a/eng/targets/Imports.targets +++ b/eng/targets/Imports.targets @@ -91,6 +91,11 @@ + + + + + - + + @@ -376,6 +381,5 @@ - diff --git a/eng/targets/PackageProject.props b/eng/targets/PackageProject.props index c2d3e0f0fb35d..02ad54dc28b47 100644 --- a/eng/targets/PackageProject.props +++ b/eng/targets/PackageProject.props @@ -11,5 +11,7 @@ $(NoWarn);NU5128 + + false \ No newline at end of file diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 40b5c278e0835..29e4ca9ea63a0 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -30,6 +30,7 @@ + @@ -39,6 +40,7 @@ + + false net46;net472 - net6.0 + net6.0;net7.0 $(MSBuildThisFileDirectory)..\config\xunit.runner.json $(MSBuildThisFileDirectory)..\config\xunit.runner.json diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs index 7b4b24d63a5c2..2ea95162a5f03 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs @@ -28,6 +28,6 @@ protected override bool IsUnnecessaryCast(SemanticModel model, ExpressionSyntax protected override TextSpan GetFadeSpan(ExpressionSyntax node) => node is CastExpressionSyntax cast ? TextSpan.FromBounds(cast.OpenParenToken.SpanStart, cast.CloseParenToken.Span.End) : - node is BinaryExpressionSyntax binary ? TextSpan.FromBounds(binary.OperatorToken.SpanStart, node.Span.End) : throw ExceptionUtilities.Unreachable; + node is BinaryExpressionSyntax binary ? TextSpan.FromBounds(binary.OperatorToken.SpanStart, node.Span.End) : throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs index faedd97361eed..3e57d390a249e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs @@ -167,7 +167,7 @@ private static bool CheckExpressionSyntactically(ExpressionSyntax expression) case ArrowExpressionClauseSyntax arrowExpression: return arrowExpression.Expression; case null: return null; - default: throw ExceptionUtilities.Unreachable; + default: throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndNullCheckDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndNullCheckDiagnosticAnalyzer.cs index e54e88e8e1a8a..6b524ed015d6a 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndNullCheckDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndNullCheckDiagnosticAnalyzer.cs @@ -69,7 +69,7 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext) { BinaryExpressionSyntax binaryExpression => (binaryExpression.Left, (SyntaxNode)binaryExpression.Right), IsPatternExpressionSyntax isPattern => (isPattern.Expression, isPattern.Pattern), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var operand = GetNullCheckOperand(comparisonLeft, comparison.Kind(), comparisonRight)?.WalkDownParentheses(); if (operand == null) diff --git a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index 9dd74bea4334a..ac51b19bbcddc 100644 --- a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -46,35 +46,25 @@ public CSharpDeclareAsNullableCodeFixProvider() public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - if (root == null) - { - return; - } - - var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - if (model == null) - { - return; - } + var cancellationToken = context.CancellationToken; - var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + var model = await context.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var node = context.Diagnostics.First().Location.FindNode(getInnermostNodeForTie: true, cancellationToken); - var declarationTypeToFix = TryGetDeclarationTypeToFix(model, node); + var declarationTypeToFix = TryGetDeclarationTypeToFix(model, node, cancellationToken); if (declarationTypeToFix == null) - { return; - } RegisterCodeFix(context, CSharpCodeFixesResources.Declare_as_nullable, GetEquivalenceKey(node, model)); } private static string GetEquivalenceKey(SyntaxNode node, SemanticModel model) { - return IsRemoteApiUsage(node, model) ? AssigningNullLiteralRemotelyEquivalenceKey : - node.IsKind(SyntaxKind.ConditionalAccessExpression) ? ConditionalOperatorEquivalenceKey : - AssigningNullLiteralLocallyEquivalenceKey; + return IsRemoteApiUsage(node, model) + ? AssigningNullLiteralRemotelyEquivalenceKey + : node.IsKind(SyntaxKind.ConditionalAccessExpression) + ? ConditionalOperatorEquivalenceKey + : AssigningNullLiteralLocallyEquivalenceKey; static bool IsRemoteApiUsage(SyntaxNode node, SemanticModel model) { @@ -104,20 +94,21 @@ static bool IsRemoteApiUsage(SyntaxNode node, SemanticModel model) } protected override async Task FixAllAsync( - Document document, ImmutableArray diagnostics, - SyntaxEditor editor, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + Document document, + ImmutableArray diagnostics, + SyntaxEditor editor, + CodeActionOptionsProvider fallbackOptions, + CancellationToken cancellationToken) { // a method can have multiple `return null;` statements, but we should only fix its return type once using var _ = PooledHashSet.GetInstance(out var alreadyHandled); - var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - if (model != null) + var model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (var diagnostic in diagnostics) { - foreach (var diagnostic in diagnostics) - { - var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); - MakeDeclarationNullable(editor, model, node, alreadyHandled); - } + var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); + MakeDeclarationNullable(editor, model, node, alreadyHandled, cancellationToken); } } @@ -127,9 +118,10 @@ protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic, Doc return equivalenceKey == GetEquivalenceKey(node, model); } - private static void MakeDeclarationNullable(SyntaxEditor editor, SemanticModel model, SyntaxNode node, HashSet alreadyHandled) + private static void MakeDeclarationNullable( + SyntaxEditor editor, SemanticModel model, SyntaxNode node, HashSet alreadyHandled, CancellationToken cancellationToken) { - var declarationTypeToFix = TryGetDeclarationTypeToFix(model, node); + var declarationTypeToFix = TryGetDeclarationTypeToFix(model, node, cancellationToken); if (declarationTypeToFix != null && alreadyHandled.Add(declarationTypeToFix)) { var fixedDeclaration = SyntaxFactory.NullableType(declarationTypeToFix.WithoutTrivia()).WithTriviaFrom(declarationTypeToFix); @@ -137,12 +129,11 @@ private static void MakeDeclarationNullable(SyntaxEditor editor, SemanticModel m } } - private static TypeSyntax? TryGetDeclarationTypeToFix(SemanticModel model, SyntaxNode node) + private static TypeSyntax? TryGetDeclarationTypeToFix( + SemanticModel model, SyntaxNode node, CancellationToken cancellationToken) { if (!IsExpressionSupported(node)) - { return null; - } if (node.Parent is (kind: SyntaxKind.ReturnStatement or SyntaxKind.YieldReturnStatement)) { @@ -161,9 +152,7 @@ SyntaxKind.IndexerDeclaration or SyntaxKind.EventDeclaration); if (containingMember == null) - { return null; - } var onYield = node.IsParentKind(SyntaxKind.YieldReturnStatement); @@ -191,72 +180,49 @@ SyntaxKind.IndexerDeclaration or } // string x = null; - if (node.Parent?.Parent?.IsParentKind(SyntaxKind.VariableDeclaration) == true) + if (node.Parent?.Parent?.Parent is VariableDeclarationSyntax variableDeclaration) { - var variableDeclaration = (VariableDeclarationSyntax)node.Parent.Parent.Parent!; - if (variableDeclaration.Variables.Count != 1) - { - // string x = null, y = null; - return null; - } - - return variableDeclaration.Type; + // string x = null, y = null; + return variableDeclaration.Variables.Count == 1 ? variableDeclaration.Type : null; } // x = null; if (node.Parent is AssignmentExpressionSyntax assignment) { - var symbol = model.GetSymbolInfo(assignment.Left).Symbol; - if (symbol is ILocalSymbol local) + var symbol = model.GetSymbolInfo(assignment.Left, cancellationToken).Symbol; + if (symbol is ILocalSymbol { DeclaringSyntaxReferences.Length: > 0 } local) { - var syntax = local.DeclaringSyntaxReferences[0].GetSyntax(); - if (syntax is VariableDeclaratorSyntax declarator && - declarator.Parent is VariableDeclarationSyntax declaration && - declaration.Variables.Count == 1) - { + var syntax = local.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + if (syntax is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Variables.Count: 1 } declaration }) return declaration.Type; - } } else if (symbol is IParameterSymbol parameter) { - return TryGetParameterTypeSyntax(parameter); + return TryGetParameterTypeSyntax(parameter, cancellationToken); } - else if (symbol is IFieldSymbol { IsImplicitlyDeclared: false } field) + else if (symbol is IFieldSymbol { IsImplicitlyDeclared: false, DeclaringSyntaxReferences.Length: > 0 } field) { // implicitly declared fields don't have DeclaringSyntaxReferences so filter them out - var syntax = field.DeclaringSyntaxReferences[0].GetSyntax(); - if (syntax is VariableDeclaratorSyntax declarator && - declarator.Parent is VariableDeclarationSyntax declaration && - declaration.Variables.Count == 1) - { + var syntax = field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + if (syntax is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Variables.Count: 1 } declaration }) return declaration.Type; - } - else if (syntax is TupleElementSyntax tupleElement) - { + + if (syntax is TupleElementSyntax tupleElement) return tupleElement.Type; - } } - else if (symbol is IFieldSymbol { CorrespondingTupleField: IFieldSymbol tupleField }) + else if (symbol is IFieldSymbol { CorrespondingTupleField: IFieldSymbol { Locations: [{ IsInSource: true } location] } }) { // Assigning a tuple field, eg. foo.Item1 = null // The tupleField won't have DeclaringSyntaxReferences because it's implicitly declared, otherwise it // would have fallen into the branch above. We can use the Locations instead, if there is one and it's in source - if (tupleField.Locations is { Length: 1 } && - tupleField.Locations[0] is { IsInSource: true } location) - { - if (location.FindNode(default) is TupleElementSyntax tupleElement) - { - return tupleElement.Type; - } - } + if (location.FindNode(cancellationToken) is TupleElementSyntax tupleElement) + return tupleElement.Type; } - else if (symbol is IPropertySymbol property) + else if (symbol is IPropertySymbol { DeclaringSyntaxReferences.Length: > 0 } property) { - var syntax = property.DeclaringSyntaxReferences[0].GetSyntax(); + var syntax = property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); if (syntax is PropertyDeclarationSyntax declaration) - { return declaration.Type; - } } return null; @@ -265,8 +231,8 @@ declarator.Parent is VariableDeclarationSyntax declaration && // Method(null) if (node.Parent is ArgumentSyntax argument && argument.Parent?.Parent is InvocationExpressionSyntax invocation) { - var symbol = model.GetSymbolInfo(invocation.Expression).Symbol; - if (symbol is not IMethodSymbol method || method.PartialImplementationPart is object) + var symbol = model.GetSymbolInfo(invocation.Expression, cancellationToken).Symbol; + if (symbol is not IMethodSymbol method || method.PartialImplementationPart is not null) { // We don't handle partial methods yet return null; @@ -275,14 +241,14 @@ declarator.Parent is VariableDeclarationSyntax declaration && if (argument.NameColon?.Name is IdentifierNameSyntax { Identifier: var identifier }) { var parameter = method.Parameters.Where(p => p.Name == identifier.Text).FirstOrDefault(); - return TryGetParameterTypeSyntax(parameter); + return TryGetParameterTypeSyntax(parameter, cancellationToken); } var index = invocation.ArgumentList.Arguments.IndexOf(argument); if (index >= 0 && index < method.Parameters.Length) { var parameter = method.Parameters[index]; - return TryGetParameterTypeSyntax(parameter); + return TryGetParameterTypeSyntax(parameter, cancellationToken); } return null; @@ -290,30 +256,23 @@ declarator.Parent is VariableDeclarationSyntax declaration && // string x { get; set; } = null; if (node.Parent?.Parent is PropertyDeclarationSyntax propertyDeclaration) - { return propertyDeclaration.Type; - } // string x { get; } // Unassigned value that's not marked as null if (node is PropertyDeclarationSyntax propertyDeclarationSyntax) - { return propertyDeclarationSyntax.Type; - } // string x; // Unassigned value that's not marked as null - if (node is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: FieldDeclarationSyntax _ } declarationSyntax } && - declarationSyntax.Variables.Count == 1) - { + if (node is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: FieldDeclarationSyntax, Variables.Count: 1 } declarationSyntax }) return declarationSyntax.Type; - } // void M(string x = null) { } if (node.Parent?.Parent is ParameterSyntax optionalParameter) { - var parameterSymbol = model.GetDeclaredSymbol(optionalParameter); - return TryGetParameterTypeSyntax(parameterSymbol); + var parameterSymbol = model.GetDeclaredSymbol(optionalParameter, cancellationToken); + return TryGetParameterTypeSyntax(parameterSymbol, cancellationToken); } // static string M() => null; @@ -350,9 +309,7 @@ declarator.Parent is VariableDeclarationSyntax declaration && case GenericNameSyntax generic: var typeArguments = generic.TypeArgumentList.Arguments; if (typeArguments.Count == 1) - { return typeArguments[0]; - } break; } @@ -360,10 +317,9 @@ declarator.Parent is VariableDeclarationSyntax declaration && return null; } - static TypeSyntax? TryGetParameterTypeSyntax(IParameterSymbol? parameterSymbol) + static TypeSyntax? TryGetParameterTypeSyntax(IParameterSymbol? parameterSymbol, CancellationToken cancellationToken) { - if (parameterSymbol is object && - parameterSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is ParameterSyntax parameterSyntax && + if (parameterSymbol?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken) is ParameterSyntax parameterSyntax && parameterSymbol.ContainingSymbol is IMethodSymbol method && method.GetAllMethodSymbolsOfPartialParts().Length == 1) { diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs index 9ab316e351749..e27e5524a7790 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Composition; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Formatting; @@ -17,7 +16,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues; -using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues @@ -57,6 +55,21 @@ protected override SyntaxNode TryUpdateNameForFlaggedNode(SyntaxNode node, Synta case SyntaxKind.VariableDeclarator: var variableDeclarator = (VariableDeclaratorSyntax)node; + if (newName.ValueText == AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName && + variableDeclarator.Initializer?.Value is ImplicitObjectCreationExpressionSyntax implicitObjectCreation && + variableDeclarator.Parent is VariableDeclarationSyntax parent) + { + // If we are generating a discard on the left of an initialization with an implicit object creation on the right, + // then we need to replace the implicit object creation with an explicit one. + // For example: 'TypeName v = new();' must be changed to '_ = new TypeName();' + var objectCreationNode = SyntaxFactory.ObjectCreationExpression( + newKeyword: implicitObjectCreation.NewKeyword, + type: parent.Type, + argumentList: implicitObjectCreation.ArgumentList, + initializer: implicitObjectCreation.Initializer); + variableDeclarator = variableDeclarator.WithInitializer(variableDeclarator.Initializer.WithValue(objectCreationNode)); + } + return variableDeclarator.WithIdentifier(newName.WithTriviaFrom(variableDeclarator.Identifier)); case SyntaxKind.SingleVariableDesignation: @@ -79,7 +92,7 @@ protected override SyntaxNode TryUpdateNameForFlaggedNode(SyntaxNode node, Synta } } - protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts) + protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts, SemanticModel semanticModel) { if (newNameNode.IsKind(SyntaxKind.DiscardDesignation) && parent is DeclarationPatternSyntax declarationPattern @@ -91,6 +104,21 @@ protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, Sy return SyntaxFactory.TypePattern(declarationPattern.Type).WithTrailingTrivia(trailingTrivia); } + else if (parent is AssignmentExpressionSyntax assignment && + assignment.Right is ImplicitObjectCreationExpressionSyntax implicitObjectCreation && + newNameNode is IdentifierNameSyntax { Identifier.ValueText: AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName } && + semanticModel.GetTypeInfo(implicitObjectCreation).Type is { } type) + { + // If we are generating a discard on the left of an assignment with an implicit object creation on the right, + // then we need to replace the implicit object creation with an explicit one. + // For example: 'v = new();' must be changed to '_ = new TypeOfV();' + var objectCreationNode = SyntaxFactory.ObjectCreationExpression( + newKeyword: implicitObjectCreation.NewKeyword, + type: type.GenerateTypeSyntax(allowVar: false), + argumentList: implicitObjectCreation.ArgumentList, + initializer: implicitObjectCreation.Initializer); + return assignment.Update((ExpressionSyntax)newNameNode, assignment.OperatorToken, objectCreationNode); + } return null; } @@ -171,7 +199,7 @@ protected override SyntaxNode GetReplacementNodeForCompoundAssignment( protected override SyntaxNode GetReplacementNodeForVarPattern(SyntaxNode originalVarPattern, SyntaxNode newNameNode) { if (originalVarPattern is not VarPatternSyntax pattern) - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); // If the replacement node is DiscardDesignationSyntax // then we need to just change the incoming var's pattern designation diff --git a/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs index 6c3690198589e..8768276dca2ab 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs @@ -128,7 +128,7 @@ private static RangeExpressionSyntax CreateRangeExpression(Result result, Syntax { ResultKind.Computed => CreateComputedRange(result), ResultKind.Constant => CreateConstantRange(result, generator), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; private static RangeExpressionSyntax CreateComputedRange(Result result) diff --git a/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs index 427a54cecdbc7..d6cf8b7be03e6 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs @@ -58,7 +58,7 @@ protected override Task FixAllAsync( UnaryPatternSyntax => s_nullConstantPattern, // The analyzer reports diagnostic only on BinaryExpressionSyntax and UnaryPatternSyntax. - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; editor.ReplaceNode(node, replacement.WithTriviaFrom(node)); diff --git a/src/Analyzers/CSharp/CodeFixes/UseUtf8StringLiteral/UseUtf8StringLiteralCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUtf8StringLiteral/UseUtf8StringLiteralCodeFixProvider.cs index 2e38282fe2e56..557bbbe3f7d65 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseUtf8StringLiteral/UseUtf8StringLiteralCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseUtf8StringLiteral/UseUtf8StringLiteralCodeFixProvider.cs @@ -102,7 +102,7 @@ private static IArrayCreationOperation GetArrayCreationOperation(SemanticModel s var operationLocationString = diagnostic.Properties[nameof(UseUtf8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation)]; if (!Enum.TryParse(operationLocationString, out UseUtf8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation operationLocation)) - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); // Because we get the location from an IOperation.Syntax, sometimes we have to look a // little harder to get back from syntax to the operation that triggered the diagnostic @@ -136,7 +136,7 @@ static IArrayCreationOperation FindArrayCreationOperationAncestor(IOperation ope operation = operation.Parent!; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs index cc7db15e377e4..07962bbcd8453 100644 --- a/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs @@ -316,5 +316,172 @@ void MyMethod() FixedState = { Sources = { fixedCode } }, }.RunAsync(); } + + [Fact] + public async Task TestRegion() + { + var testCode = @" +class MyClass +{ +#if true + public void M() + { + #region ABC1 + System.Console.WriteLine(); + #endregion + } +#else + public void M() + { + #region ABC2 + System.Console.WriteLine(); + #endregion + } +#endif +} +"; + await Verify.VerifyCodeFixAsync(testCode, testCode); + } + + [Fact] + public async Task TestRegion2() + { + var testCode = @" +class MyClass +{ +#if true + public void M() + { +[||]#region OUTER1 + #region ABC1 + System.Console.WriteLine(); + #endregion +[||]#endregion + } +#else + public void M() + { +#region OUTER2 + #region ABC2 + System.Console.WriteLine(); + #endregion +#endregion + } +#endif +} +"; + + var fixedCode = @" +class MyClass +{ +#if true + public void M() + { + #region OUTER1 + #region ABC1 + System.Console.WriteLine(); + #endregion + #endregion + } +#else + public void M() + { +#region OUTER2 + #region ABC2 + System.Console.WriteLine(); + #endregion +#endregion + } +#endif +} +"; + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestRegion3() + { + var testCode = @" +class MyClass +{ +#if true + public void M() + { +[||]#region ABC1 + System.Console.WriteLine(); +[||]#endregion + } +#else + public void M() + { +#region ABC2 + System.Console.WriteLine(); +#endregion + } +#endif +} +"; + var fixedCode = @" +class MyClass +{ +#if true + public void M() + { + #region ABC1 + System.Console.WriteLine(); + #endregion + } +#else + public void M() + { +#region ABC2 + System.Console.WriteLine(); +#endregion + } +#endif +} +"; + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestRegion4() + { + var testCode = @" +class MyClass +{ +#if true + public void M() + { +[||]#region ABC1 + System.Console.WriteLine(); +[||]#endregion + } +#else + #region ABC2 + public void M() { } + #endregion +#endif +} +"; + var fixedCode = @" +class MyClass +{ +#if true + public void M() + { + #region ABC1 + System.Console.WriteLine(); + #endregion + } +#else + #region ABC2 + public void M() { } + #endregion +#endif +} +"; + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index 1384f6399e704..671d5bc9a4ae8 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -493,7 +493,7 @@ int M() return x; } }", iterations: 1), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test @@ -551,7 +551,7 @@ int M() return x; } }", iterations: 1), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test @@ -1066,7 +1066,7 @@ int M(int {|IDE0060:x|}, int {|IDE0060:y|}) return x; } }", iterations: 1), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var test = new VerifyCS.Test @@ -1161,7 +1161,7 @@ int M(int {|IDE0060:x|}, int {|IDE0060:y|}) int M2() => 0; }", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var test = new VerifyCS.Test @@ -1240,7 +1240,7 @@ void M(int x) _ = {prefix}x{postfix}; }} }}", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var test = new VerifyCS.Test @@ -1357,7 +1357,7 @@ void M(int x) int M2() => 0; }}", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var test = new VerifyCS.Test @@ -2571,7 +2571,7 @@ void M(object p) }; } }", iterations: 1), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test @@ -2770,7 +2770,7 @@ bool M(object p1, object p2) return isZero; } }", - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; await new VerifyCS.Test @@ -2895,7 +2895,7 @@ bool M2(out int x) return false; } }", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test @@ -3046,7 +3046,7 @@ bool M2(int {|IDE0060:x|}) return false; } }", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var test = new VerifyCS.Test @@ -7805,7 +7805,7 @@ void M(object p) }; } }", iterations: 1), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test @@ -8846,5 +8846,73 @@ void M() } }", optionName); } + + [Fact] + [WorkItem(64291, "https://github.com/dotnet/roslyn/issues/64291")] + public async Task TestImplicitObjectCreationInInitialization() + { + var source = +@"class C +{ + void M() + { + C {|IDE0059:c|} = new(); + } +}"; + var fixedSource = +@"class C +{ + void M() + { + _ = new C(); + } +}"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + Options = + { + { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable }, + }, + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } + + [Fact] + [WorkItem(64291, "https://github.com/dotnet/roslyn/issues/64291")] + public async Task TestImplicitObjectCreationInAssignement() + { + var source = +@"class C +{ + void M(C c) + { + System.Console.WriteLine(c); + {|IDE0059:c|} = new(); + } +}"; + var fixedSource = +@"class C +{ + void M(C c) + { + System.Console.WriteLine(c); + _ = new C(); + } +}"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + Options = + { + { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable }, + }, + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } } } diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs index a969cb9269afa..774adb6626819 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs @@ -77,6 +77,64 @@ void M(object x) }.RunAsync(); } + [Fact, WorkItem(64292, "https://github.com/dotnet/roslyn/issues/64292")] + public async Task BooleanValueConstantPattern() + { + await new VerifyCS.Test + { + TestCode = +@"class C +{ + void M(bool x) + { + if (!(x [|is|] true)) + { + } + } +}", + FixedCode = +@"class C +{ + void M(bool x) + { + if (x is false) + { + } + } +}", + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } + + [Fact, WorkItem(64292, "https://github.com/dotnet/roslyn/issues/64292")] + public async Task NonBooleanValueConstantPattern() + { + await new VerifyCS.Test + { + TestCode = +@"class C +{ + void M(object x) + { + if (!(x [|is|] true)) + { + } + } +}", + FixedCode = +@"class C +{ + void M(object x) + { + if (x is not true) + { + } + } +}", + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } + [Fact, WorkItem(46699, "https://github.com/dotnet/roslyn/issues/46699")] public async Task UseNotPattern() { diff --git a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx index 2fe0d502ea103..2b95225ec9034 100644 --- a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx +++ b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx @@ -241,8 +241,11 @@ Convert to conditional expression - - Use coalesce expression + + Use coalesce expression for non-nullable types + + + Use coalesce expression for nullable types Changes to expression trees may result in behavior changes at runtime diff --git a/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs b/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs index cbacab08de4a6..9602b39e82a46 100644 --- a/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs +++ b/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs @@ -273,7 +273,7 @@ public static Diagnostic CreateWithMessage( } Debug.Assert(id.StartsWith("IDE", StringComparison.Ordinal)); - return $"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{id.ToLowerInvariant()}"; + return $"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{id.ToLowerInvariant()}"; } public sealed class LocalizableStringWithArguments : LocalizableString, IObjectWritable diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs index 8f8c2f0b8f667..59e5a75488ce8 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Operations; @@ -53,22 +54,18 @@ public bool IsSuppressMessageAttributeWithNamedArguments( CancellationToken cancellationToken, out ImmutableArray<(string name, IOperation value)> namedAttributeArguments) { - var attribute = model.GetOperation(attributeSyntax, cancellationToken); - if (attribute == null) + var operation = (model.GetOperation(attributeSyntax, cancellationToken) as IAttributeOperation)?.Operation; + if (operation is not IObjectCreationOperation { Initializer: { } initializerOperation }) { namedAttributeArguments = ImmutableArray<(string name, IOperation value)>.Empty; return false; } - // Workaround for https://github.com/dotnet/roslyn/issues/18198 - // Use 'IOperation.Children' to get named attribute arguments. - // Each named attribute argument is represented as an 'ISimpleAssignmentOperation' - // with a constant value assignment to an 'IPropertyReferenceOperation' in the operation tree. using var _ = ArrayBuilder<(string name, IOperation value)>.GetInstance(out var builder); - foreach (var childOperation in attribute.ChildOperations) + foreach (var initializer in initializerOperation.Initializers) { - if (childOperation is ISimpleAssignmentOperation simpleAssignment && - simpleAssignment.Target is IPropertyReferenceOperation propertyReference && + var simpleAssignment = (ISimpleAssignmentOperation)initializer; + if (simpleAssignment.Target is IPropertyReferenceOperation propertyReference && _suppressMessageAttributeType.Equals(propertyReference.Property.ContainingType)) { builder.Add((propertyReference.Property.Name, simpleAssignment.Value)); diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 52fb5b1a6786e..4ced79a25386e 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -183,27 +183,14 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. - // 3. Operations with OperationKind.None which are not operation root nodes. Attributes - // generate operation blocks with root operation with OperationKind.None, and we don't want to bail out for them. - symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, + // 3. Operations with OperationKind.None. + symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None, OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); - symbolStartContext.RegisterOperationAction(AnalyzeOperationNone, OperationKind.None); symbolStartContext.RegisterSymbolEndAction(symbolEndContext => OnSymbolEnd(symbolEndContext, hasUnsupportedOperation)); // Register custom language-specific actions, if any. _analyzer.HandleNamedTypeSymbolStart(symbolStartContext, onSymbolUsageFound); - - return; - - void AnalyzeOperationNone(OperationAnalysisContext context) - { - if (context.Operation.Kind == OperationKind.None && - context.Operation.Parent != null) - { - hasUnsupportedOperation = true; - } - } }, SymbolKind.NamedType); } diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs index 50d34cba2d253..17a689972a9d3 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedParametersAndValues/AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.SymbolStartAnalyzer.BlockAnalyzer.cs @@ -402,9 +402,9 @@ private bool ShouldAnalyze(IOperation operationBlock, ISymbol owningSymbol, ref { switch (operationBlock.Kind) { - case OperationKind.None: + case OperationKind.Attribute: case OperationKind.ParameterInitializer: - // Skip blocks from attributes (which have OperationKind.None) and parameter initializers. + // Skip blocks from attributes and parameter initializers. // We don't have any unused values in such operation blocks. return false; diff --git a/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs index ae2ed8dc17b39..cf3bc70260a1d 100644 --- a/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs @@ -26,7 +26,7 @@ protected AbstractUseCoalesceExpressionDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId, EnforceOnBuildValues.UseCoalesceExpression, CodeStyleOptions2.PreferCoalesceExpression, - new LocalizableResourceString(nameof(AnalyzersResources.Use_coalesce_expression), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), + new LocalizableResourceString(nameof(AnalyzersResources.Use_coalesce_expression_for_non_nullable_types), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), new LocalizableResourceString(nameof(AnalyzersResources.Null_check_can_be_simplified), AnalyzersResources.ResourceManager, typeof(AnalyzersResources))) { } diff --git a/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs index 6bbc28f17449d..73811303fb44d 100644 --- a/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCoalesceExpression/AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs @@ -28,7 +28,7 @@ protected AbstractUseCoalesceExpressionForNullableDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseCoalesceExpressionForNullableDiagnosticId, EnforceOnBuildValues.UseCoalesceExpressionForNullable, CodeStyleOptions2.PreferCoalesceExpression, - new LocalizableResourceString(nameof(AnalyzersResources.Use_coalesce_expression), AnalyzersResources.ResourceManager, typeof(AnalyzersResources))) + new LocalizableResourceString(nameof(AnalyzersResources.Use_coalesce_expression_for_nullable_types), AnalyzersResources.ResourceManager, typeof(AnalyzersResources))) { } diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf index dbe39341d7a28..34649f526b410 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf @@ -372,9 +372,14 @@ Použít automatickou vlastnost - - Use coalesce expression - Použít slučovací výraz + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf index 7a5c52184e429..6e20444003145 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf @@ -372,9 +372,14 @@ Automatisch generierte Eigenschaft verwenden - - Use coalesce expression - COALESCE-Ausdruck verwenden + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf index 96374efdf6de1..b7808c755aa85 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf @@ -372,9 +372,14 @@ Usar propiedad automática - - Use coalesce expression - Usar expresión coalesce + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf index 38d06337e925c..165f75f2a59bf 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf @@ -372,9 +372,14 @@ Utiliser auto-property - - Use coalesce expression - Utiliser l'expression de fusion + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf index d995aee740491..387574e6d34e2 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf @@ -372,9 +372,14 @@ Usa la proprietà automatica - - Use coalesce expression - Usa l'espressione COALESCE + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf index 1ce40775d3446..b028629812d98 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf @@ -372,9 +372,14 @@ 自動プロパティを使用する - - Use coalesce expression - coalesce 式を使用します + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf index 3055f27049dff..2839e06ecd69b 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf @@ -372,9 +372,14 @@ auto 속성 사용 - - Use coalesce expression - COALESCE 식 사용 + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf index 67e6629342339..3d7aa2b5fb641 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf @@ -372,9 +372,14 @@ Użyj właściwości automatycznej - - Use coalesce expression - Użyj wyrażenia łączącego + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf index e1bd13c2fe484..e4bb1a9d78802 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf @@ -372,9 +372,14 @@ Usar a propriedade auto - - Use coalesce expression - Usar a expressão de união + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf index a8a8331b34ae1..0fc5b6455f248 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf @@ -372,9 +372,14 @@ Использовать свойство auto - - Use coalesce expression - Используйте выражение объединения + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf index dba5285e7521b..b360d981e938d 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf @@ -372,9 +372,14 @@ Otomatik özellik kullan - - Use coalesce expression - Birleştirme ifadesi kullan + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf index e92f32eb09486..8cd403761aae1 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf @@ -372,9 +372,14 @@ 使用自动属性 - - Use coalesce expression - 使用 COALESCE 表达式 + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf index 838e98805701d..45dcea01c1118 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf @@ -372,9 +372,14 @@ 使用 Auto 屬性 - - Use coalesce expression - 使用 coalesce 運算式 + + Use coalesce expression for non-nullable types + Use coalesce expression for non-nullable types + + + + Use coalesce expression for nullable types + Use coalesce expression for nullable types diff --git a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs index 4cf9189e75c35..21fceb99e256f 100644 --- a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs @@ -41,7 +41,7 @@ protected sealed override void FixOneDiagnostic( } protected sealed override ITypeSymbol GetSwitchType(ISwitchExpressionOperation switchExpression) - => switchExpression.Value.Type ?? throw ExceptionUtilities.Unreachable; + => switchExpression.Value.Type ?? throw ExceptionUtilities.Unreachable(); protected sealed override ICollection GetMissingEnumMembers(ISwitchExpressionOperation switchOperation) => PopulateSwitchExpressionHelpers.GetMissingEnumMembers(switchOperation); diff --git a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchStatementCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchStatementCodeFixProvider.cs index c742996ebe5ad..8fe0a1960550f 100644 --- a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchStatementCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchStatementCodeFixProvider.cs @@ -57,7 +57,7 @@ protected sealed override void FixOneDiagnostic( } protected sealed override ITypeSymbol GetSwitchType(ISwitchOperation switchOperation) - => switchOperation.Value.Type ?? throw ExceptionUtilities.Unreachable; + => switchOperation.Value.Type ?? throw ExceptionUtilities.Unreachable(); protected sealed override ICollection GetMissingEnumMembers(ISwitchOperation switchOperation) => PopulateSwitchStatementHelpers.GetMissingEnumMembers(switchOperation); diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs index 50e27fe76c1e3..ff66a335a3136 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs @@ -117,9 +117,10 @@ protected abstract SyntaxNode GetReplacementNodeForCompoundAssignment( /// The rewritten node produced by . /// The syntax editor for the code fix. /// The syntax facts for the current language. + /// Semantic model for the tree. /// The replacement node to use in the rewritten syntax tree; otherwise, to only /// rewrite the node originally rewritten by . - protected virtual SyntaxNode? TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts) => null; + protected virtual SyntaxNode? TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts, SemanticModel semanticModel) => null; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -363,7 +364,7 @@ await FixAllValueAssignedIsUnusedDiagnosticsAsync( break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -554,7 +555,7 @@ private async Task FixAllValueAssignedIsUnusedDiagnosticsAsync( } else { - var newParentNode = TryUpdateParentOfUpdatedNode(node.GetRequiredParent(), newNameNode, editor, syntaxFacts); + var newParentNode = TryUpdateParentOfUpdatedNode(node.GetRequiredParent(), newNameNode, editor, syntaxFacts, semanticModel); if (newParentNode is not null) { nodeReplacementMap.Add(node.GetRequiredParent(), newParentNode); diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs index 7f16853543de6..4199da1d4392c 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs @@ -35,7 +35,7 @@ protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic) public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, AnalyzersResources.Use_coalesce_expression, nameof(AnalyzersResources.Use_coalesce_expression)); + RegisterCodeFix(context, AnalyzersResources.Use_coalesce_expression_for_non_nullable_types, nameof(AnalyzersResources.Use_coalesce_expression_for_non_nullable_types)); return Task.CompletedTask; } diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs index 0ff82175f3db5..959f85bbd9d82 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs @@ -34,7 +34,7 @@ protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic) public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, AnalyzersResources.Use_coalesce_expression, nameof(AnalyzersResources.Use_coalesce_expression)); + RegisterCodeFix(context, AnalyzersResources.Use_coalesce_expression_for_nullable_types, nameof(AnalyzersResources.Use_coalesce_expression_for_nullable_types)); return Task.CompletedTask; } diff --git a/src/CodeStyle/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj b/src/CodeStyle/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj index 2fdb18fb43146..3c72c53510748 100644 --- a/src/CodeStyle/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj +++ b/src/CodeStyle/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj @@ -6,7 +6,9 @@ Microsoft.CodeAnalysis.CSharp netstandard2.0 $(DefineConstants),CODE_STYLE - Microsoft.CodeAnalysis.CSharp.CodeStyle.NewNameSinceWeReferenceTheAnalyzersAndNuGetCannotFigureItOut + + + true diff --git a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj index e9db13b64fa61..823ae3c272bd4 100644 --- a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj +++ b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj @@ -8,6 +8,9 @@ $(DefineConstants),CODE_STYLE true true + + + true diff --git a/src/CodeStyle/Core/CodeFixes/Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj b/src/CodeStyle/Core/CodeFixes/Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj index 4b21116e59957..256a85df30982 100644 --- a/src/CodeStyle/Core/CodeFixes/Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj +++ b/src/CodeStyle/Core/CodeFixes/Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj @@ -6,6 +6,9 @@ Microsoft.CodeAnalysis netstandard2.0 $(DefineConstants),CODE_STYLE + + + true diff --git a/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj b/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj index 91956df402f6b..6a3b665477240 100644 --- a/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj +++ b/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj @@ -5,6 +5,7 @@ true false true + false diff --git a/src/CodeStyle/VisualBasic/Analyzers/Microsoft.CodeAnalysis.VisualBasic.CodeStyle.vbproj b/src/CodeStyle/VisualBasic/Analyzers/Microsoft.CodeAnalysis.VisualBasic.CodeStyle.vbproj index cb9bb6dc23773..93ac5bfd60654 100644 --- a/src/CodeStyle/VisualBasic/Analyzers/Microsoft.CodeAnalysis.VisualBasic.CodeStyle.vbproj +++ b/src/CodeStyle/VisualBasic/Analyzers/Microsoft.CodeAnalysis.VisualBasic.CodeStyle.vbproj @@ -6,7 +6,9 @@ netstandard2.0 $(DefineConstants),CODE_STYLE - Microsoft.CodeAnalysis.VisualBasic.CodeStyle.NewNameSinceWeReferenceTheAnalyzersAndNuGetCannotFigureItOut + + + true diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index d4b69a43728e3..fec144cb2a8c9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -75,7 +75,7 @@ protected override BoundExpression GetLambdaExpressionBody(BoundBlock body) protected override BoundBlock CreateBlockFromLambdaExpressionBody(Binder lambdaBodyBinder, BoundExpression expression, BindingDiagnosticBag diagnostics) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 85ca05713c6ae..fad2311e3aedf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -2026,7 +2026,7 @@ static void getArgList( /// whether analysis should consider value or ref escape. Not all method arguments /// are included, and some arguments may be included twice - once for value, once for ref. /// - private void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( + private static void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( Symbol symbol, BoundExpression? receiver, ImmutableArray parameters, @@ -2102,7 +2102,7 @@ static bool hasRefLikeReturn(Symbol symbol) /// Optionally this will also return all of the that /// result from this invocation. That is useful for MAMM analysis. /// - private void GetEscapeValuesForUpdatedRules( + private static void GetEscapeValuesForUpdatedRules( Symbol symbol, BoundExpression? receiver, ImmutableArray parameters, @@ -2687,7 +2687,7 @@ private static void ReportReadOnlyError(Symbol symbol, SyntaxNode node, BindValu }; int index = (checkingReceiver ? 3 : 0) + (kind == BindValueKind.RefReturn ? 0 : (RequiresRefOrOut(kind) ? 1 : 2)); - Error(diagnostics, ReadOnlyErrors[index], node, symbolKind, symbol); + Error(diagnostics, ReadOnlyErrors[index], node, symbolKind, new FormattedSymbol(symbol, SymbolDisplayFormat.ShortFormat)); } /// diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index eaf71ea3657e0..70390a2a15383 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -409,9 +409,10 @@ private void FailRemainingInferencesAndSetValEscape(ArrayBuilder @@ -4605,7 +4644,7 @@ private BoundObjectInitializerExpressionBase BindInitializerExpression( return BindCollectionInitializerExpression(syntax, type, diagnostics, implicitReceiver); default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs index c2e6d6bd44e09..fe8397b7765ee 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs @@ -146,7 +146,7 @@ internal static void BindRegularCSharpFieldInitializers( break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 25145ec3ad00c..455be2871a30d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1863,13 +1863,6 @@ private bool TryBindNameofOperator(InvocationExpressionSyntax node, BindingDiagn if (node.MayBeNameofOperator()) { var binder = this.GetBinder(node); - if (binder is null) - { - // This could happen during speculation due to a bug - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - result = null; - return false; - } if (binder.EnclosingNameofArgument == node.ArgumentList.Arguments[0].Expression) { result = binder.BindNameofOperatorInternal(node, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index f6d88eb342d3b..fe66c8b9babee 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1372,7 +1372,7 @@ FileIdentifier getFileIdentifierForFileTypes() } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 67cce30d275fe..03c5813e80ca7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -4335,8 +4335,13 @@ private static ConstantValue FoldConditionalOperator(BoundExpression condition, } } - private static void CheckNativeIntegerFeatureAvailability(BinaryOperatorKind operatorKind, SyntaxNode syntax, BindingDiagnosticBag diagnostics) + private void CheckNativeIntegerFeatureAvailability(BinaryOperatorKind operatorKind, SyntaxNode syntax, BindingDiagnosticBag diagnostics) { + if (Compilation.Assembly.RuntimeSupportsNumericIntPtr) + { + return; + } + switch (operatorKind & BinaryOperatorKind.TypeMask) { case BinaryOperatorKind.NInt: @@ -4346,8 +4351,13 @@ private static void CheckNativeIntegerFeatureAvailability(BinaryOperatorKind ope } } - private static void CheckNativeIntegerFeatureAvailability(UnaryOperatorKind operatorKind, SyntaxNode syntax, BindingDiagnosticBag diagnostics) + private void CheckNativeIntegerFeatureAvailability(UnaryOperatorKind operatorKind, SyntaxNode syntax, BindingDiagnosticBag diagnostics) { + if (Compilation.Assembly.RuntimeSupportsNumericIntPtr) + { + return; + } + switch (operatorKind & UnaryOperatorKind.TypeMask) { case UnaryOperatorKind.NInt: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index abaf95f3a1c82..87aa77fca3797 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -72,7 +72,7 @@ private BoundExpression MakeIsPatternExpression( case BoundConstantPattern _: case BoundITuplePattern _: // these patterns can fail in practice - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); case BoundRelationalPattern _: case BoundTypePattern _: case BoundNegatedPattern _: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b4598a801e866..ecf4ddfb87653 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1542,6 +1542,11 @@ private BoundAssignmentOperator BindAssignment( { if (isRef) { + // https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/low-level-struct-improvements.md#rules-ref-reassignment + // For a ref reassignment in the form `e1 = ref e2` both of the following must be true: + // 1. `e2` must have *ref-safe-to-escape* at least as large as the *ref-safe-to-escape* of `e1` + // 2. `e1` must have the same *safe-to-escape* as `e2` + var leftEscape = GetRefEscape(op1, LocalScopeDepth); var rightEscape = GetRefEscape(op2, LocalScopeDepth); if (leftEscape < rightEscape) @@ -1560,6 +1565,28 @@ private BoundAssignmentOperator BindAssignment( op2 = ToBadExpression(op2); } } + else if (op1.Kind is BoundKind.Local or BoundKind.Parameter) + { + leftEscape = GetValEscape(op1, LocalScopeDepth); + rightEscape = GetValEscape(op2, LocalScopeDepth); + + Debug.Assert(leftEscape == rightEscape || op1.Type.IsRefLikeType); + + // We only check if the safe-to-escape of e2 is wider than the safe-to-escape of e1 here, + // we don't check for equality. The case where the safe-to-escape of e2 is narrower than + // e1 is handled in the if (op1.Type.IsRefLikeType) { ... } block later. + if (leftEscape > rightEscape) + { + Debug.Assert(op1.Kind != BoundKind.Parameter); // If the assert fails, add a corresponding test. + + var errorCode = this.InUnsafeRegion ? ErrorCode.WRN_RefAssignValEscapeWider : ErrorCode.ERR_RefAssignValEscapeWider; + Error(diagnostics, errorCode, node, getName(op1), op2.Syntax); + if (!this.InUnsafeRegion) + { + op2 = ToBadExpression(op2); + } + } + } } if (op1.Type.IsRefLikeType) @@ -3761,7 +3788,7 @@ internal virtual BoundExpressionStatement BindConstructorInitializer(Constructor break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Binder/BlockBinder.cs b/src/Compilers/CSharp/Portable/Binder/BlockBinder.cs index 78be648c8c5d2..52ea1db8a9384 100644 --- a/src/Compilers/CSharp/Portable/Binder/BlockBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/BlockBinder.cs @@ -70,7 +70,7 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator @@ -88,7 +88,7 @@ internal override ImmutableArray GetDeclaredLocalFunctionsF return this.LocalFunctions; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs index ee98266fc1a45..7f328e0efd427 100644 --- a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs @@ -154,7 +154,7 @@ internal override BoundExpression? ConditionalReceiverExpression internal override TypeWithAnnotations GetIteratorElementType() { // There's supposed to be an enclosing method or lambda. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override Symbol? ContainingMemberOrLambda @@ -177,78 +177,78 @@ internal override bool AreNullableAnnotationsGloballyEnabled() internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundStatement BindSwitchStatementCore(SwitchStatementSyntax node, Binder originalBinder, BindingDiagnosticBag diagnostics) { // There's supposed to be a SwitchBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSyntax node, Binder originalBinder, BindingDiagnosticBag diagnostics) { // There's supposed to be a SwitchExpressionBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabelSyntax node, BindingDiagnosticBag diagnostics) { // There's supposed to be a SwitchBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, TypeSymbol switchGoverningType, uint switchGoverningValEscape, BindingDiagnosticBag diagnostics) { // There's supposed to be an overrider of this method (e.g. SwitchExpressionArmBinder) for the arm in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundForStatement BindForParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a ForLoopBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundStatement BindForEachParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a ForEachLoopBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundStatement BindForEachDeconstruction(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a ForEachLoopBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundWhileStatement BindWhileParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a WhileBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundDoStatement BindDoParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a WhileBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundStatement BindUsingStatementParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a UsingStatementBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { // There's supposed to be a LockBinder (or other overrider of this method) in the chain. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableHashSet LockedOrDisposedVariables diff --git a/src/Compilers/CSharp/Portable/Binder/CatchClauseBinder.cs b/src/Compilers/CSharp/Portable/Binder/CatchClauseBinder.cs index 51a9e7a93f7ec..d966b29e2b6f3 100644 --- a/src/Compilers/CSharp/Portable/Binder/CatchClauseBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/CatchClauseBinder.cs @@ -49,12 +49,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs index 26afb3c266367..3b5873996b3f4 100644 --- a/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ContextualAttributeBinder.cs @@ -49,7 +49,7 @@ internal Symbol AttributedMember /// /// Walk up to the nearest method/property/event. /// - private static Symbol GetAttributedMember(Symbol symbol) + internal static Symbol GetAttributedMember(Symbol symbol) { for (; (object)symbol != null; symbol = symbol.ContainingSymbol) { diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index 966fe7bd991f5..557d1880d333b 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -1396,7 +1396,7 @@ other is not (BoundDagNonNullTest or BoundDagExplicitNullTest) && // We should've skipped all type evaluations at this point. case (BoundDagTypeEvaluation, _): case (_, BoundDagTypeEvaluation): - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); // If we have found two identical evaluations as the source (possibly null), inputs can be considered related. case var (s1, s2) when s1 == s2: @@ -1868,7 +1868,7 @@ public StateForCase( public override bool Equals(object? obj) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public bool Equals(StateForCase other) @@ -1919,7 +1919,7 @@ public abstract void Filter( out Tests whenTrue, out Tests whenFalse, ref bool foundExplicitNullTest); - public virtual BoundDagTest ComputeSelectedTest() => throw ExceptionUtilities.Unreachable; + public virtual BoundDagTest ComputeSelectedTest() => throw ExceptionUtilities.Unreachable(); public virtual Tests RemoveEvaluation(BoundDagEvaluation e) => this; /// /// Rewrite nested length tests in slice subpatterns to check the top-level length property instead. diff --git a/src/Compilers/CSharp/Portable/Binder/EarlyWellKnownAttributeBinder.cs b/src/Compilers/CSharp/Portable/Binder/EarlyWellKnownAttributeBinder.cs index 675b9bbca890b..72b24057ce660 100644 --- a/src/Compilers/CSharp/Portable/Binder/EarlyWellKnownAttributeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/EarlyWellKnownAttributeBinder.cs @@ -81,6 +81,7 @@ internal static bool CanBeValidAttributeArgument(ExpressionSyntax node, Binder t case SyntaxKind.NumericLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.Utf8StringLiteralExpression: + case SyntaxKind.InterpolatedStringExpression: case SyntaxKind.CharacterLiteralExpression: case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: diff --git a/src/Compilers/CSharp/Portable/Binder/EmbeddedStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/EmbeddedStatementBinder.cs index b66c7fc215caf..e1305e287f375 100644 --- a/src/Compilers/CSharp/Portable/Binder/EmbeddedStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/EmbeddedStatementBinder.cs @@ -73,7 +73,7 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator @@ -91,7 +91,7 @@ internal override ImmutableArray GetDeclaredLocalFunctionsF return this.LocalFunctions; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionListVariableBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionListVariableBinder.cs index 600a75a445d68..0d6e275c6ec62 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionListVariableBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionListVariableBinder.cs @@ -45,12 +45,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableBinder.cs index 4f16133aa41b9..f8671f97a6058 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableBinder.cs @@ -38,12 +38,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs index b88f7e725b1a2..12504562e5a8f 100644 --- a/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/FixedStatementBinder.cs @@ -64,12 +64,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index e2e4b5945ab6d..520107e02a1c6 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1683,12 +1683,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs index 11f68d5e373b6..89ac450e57f9c 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs @@ -136,12 +136,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs b/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs index 784e159641413..23e8181eea18c 100644 --- a/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs +++ b/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs @@ -88,7 +88,7 @@ BoundWhenDecisionDagNode w when distance(w.WhenTrue) is var trueDist2 && distanc n = w.WhenFalse; break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -555,7 +555,7 @@ private static string SampleValueString(IValueSet remainingValues, TypeSymbol ty return $"> ({type.ToDisplayString()})uint.MaxValue"; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static string ValueString(ConstantValue value, TypeSymbol type, bool requireExactType) diff --git a/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs index e701dfdf57871..18877b0f0425f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs @@ -41,12 +41,12 @@ internal override bool IsLabelsScopeBinder internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Labels potentially shared across multiple ScriptLocalScopeBinder instances. diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index 1d87d01d8b315..9af64b2054d6d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -38,25 +38,25 @@ protected override ConversionsBase WithNullabilityCore(bool includeNullability) public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Conversions involving method groups require a Binder. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Conversion GetMethodGroupFunctionPointerConversion(BoundMethodGroup source, FunctionPointerTypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Conversions involving method groups require a Binder. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Conversions involving stackalloc expressions require a Binder. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override Conversion GetInterpolatedStringConversion(BoundExpression source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Conversions involving interpolated strings require a Binder. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override CSharpCompilation Compilation => null; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs index 64de9154fbc1a..46ded8b36bc3f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs @@ -38,13 +38,13 @@ public bool HasValue public override bool Equals(object obj) { // implement if needed - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int GetHashCode() { // implement if needed - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public static BinaryOperatorAnalysisResult Applicable(BinaryOperatorSignature signature, Conversion leftConversion, Conversion rightConversion) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index c61124e8316d6..def7395f182a8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -50,12 +50,12 @@ private MemberAnalysisResult( public override bool Equals(object obj) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int GetHashCode() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public Conversion ConversionForArg(int arg) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 786c9ae505ad1..324afce093b87 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2672,7 +2672,7 @@ public override BoundNode Visit(BoundNode node) protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) diff --git a/src/Compilers/CSharp/Portable/Binder/SimpleLocalScopeBinder.cs b/src/Compilers/CSharp/Portable/Binder/SimpleLocalScopeBinder.cs index d7263e00a8711..119ac348b2ae9 100644 --- a/src/Compilers/CSharp/Portable/Binder/SimpleLocalScopeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SimpleLocalScopeBinder.cs @@ -29,12 +29,12 @@ protected override ImmutableArray BuildLocals() internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/SimpleProgramBinder.cs b/src/Compilers/CSharp/Portable/Binder/SimpleProgramBinder.cs index a0c3a64df9f52..a7cfc5716f1cf 100644 --- a/src/Compilers/CSharp/Portable/Binder/SimpleProgramBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SimpleProgramBinder.cs @@ -91,7 +91,7 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator @@ -109,7 +109,7 @@ internal override ImmutableArray GetDeclaredLocalFunctionsF return this.LocalFunctions; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs index 5d193953c36c0..4538cd623a088 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder.cs @@ -330,7 +330,7 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) @@ -340,7 +340,7 @@ internal override ImmutableArray GetDeclaredLocalFunctionsF return this.LocalFunctions; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 4abc6cd60361e..fff7a1825be72 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -297,12 +297,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/WhileBinder.cs b/src/Compilers/CSharp/Portable/Binder/WhileBinder.cs index d3b86d32cab06..1a3d40306d510 100644 --- a/src/Compilers/CSharp/Portable/Binder/WhileBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WhileBinder.cs @@ -73,12 +73,12 @@ internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNo return this.Locals; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator diff --git a/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs index d1e64860628b2..93258d1b8b629 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs @@ -92,7 +92,7 @@ internal bool IsUsingAlias(string name, bool callerIsSemanticModel, ConsList GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override uint LocalScopeDepth => Binder.CurrentMethodScope; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 4b2c1a5311faf..f9325fb59db78 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -134,7 +134,7 @@ internal partial class BoundValuePlaceholderBase internal partial class BoundValuePlaceholder { - public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } internal partial class BoundInterpolatedStringHandlerPlaceholder @@ -149,7 +149,7 @@ internal partial class BoundDeconstructValuePlaceholder internal partial class BoundTupleOperandPlaceholder { - public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } internal partial class BoundAwaitableValuePlaceholder @@ -169,7 +169,7 @@ internal partial class BoundObjectOrCollectionValuePlaceholder internal partial class BoundImplicitIndexerValuePlaceholder { - public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } internal partial class BoundListPatternReceiverPlaceholder @@ -179,7 +179,7 @@ internal partial class BoundListPatternReceiverPlaceholder internal partial class BoundListPatternIndexPlaceholder { - public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } internal partial class BoundSlicePatternReceiverPlaceholder @@ -189,7 +189,7 @@ internal partial class BoundSlicePatternReceiverPlaceholder internal partial class BoundSlicePatternRangePlaceholder { - public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } internal partial class BoundThisReference diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs index 377d2e008b8b5..b1645affc7569 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs @@ -10,6 +10,6 @@ internal partial class BoundInterpolatedStringArgumentPlaceholder public const int TrailingConstructorValidityParameter = -2; public const int UnspecifiedParameter = -3; - public sealed override bool IsEquivalentToThisReference => throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + public sealed override bool IsEquivalentToThisReference => throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs index 52cc65546c387..de55f90b77792 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs @@ -252,7 +252,7 @@ public static InterpolatedStringHandlerData GetInterpolatedStringHandlerData(thi BoundBinaryOperator { InterpolatedStringHandlerData: { } d } => d, BoundInterpolatedString { InterpolationData: { } d } => d, BoundBinaryOperator or BoundInterpolatedString when !throwOnMissing => default, - BoundBinaryOperator or BoundInterpolatedString => throw ExceptionUtilities.Unreachable, + BoundBinaryOperator or BoundInterpolatedString => throw ExceptionUtilities.Unreachable(), _ => throw ExceptionUtilities.UnexpectedValue(e.Kind), }; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundStatementExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundStatementExtensions.cs index b464d0ee79e00..df8bb05151434 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundStatementExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundStatementExtensions.cs @@ -48,7 +48,7 @@ internal static void AssertIsLabeledStatementWithLabel(this BoundStatement node, return; } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); default: throw ExceptionUtilities.UnexpectedValue(node.Kind); diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index b323c0ebe9479..f801e1d21d2cf 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -105,7 +105,7 @@ internal sealed partial class BoundPropertyGroup { public override object Display { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } } @@ -137,7 +137,7 @@ internal partial class DeconstructionVariablePendingInference { public override object Display { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs index db5389f0b60c1..1438c25004786 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs @@ -121,7 +121,8 @@ Symbol remapLambda(BoundLambda boundLambda, LambdaSymbol lambda) if (updatedDelegateType is null) { Debug.Assert(updatedContaining is object); - updatedLambda = boundLambda.CreateLambdaSymbol(updatedContaining, lambda.ReturnTypeWithAnnotations, lambda.ParameterTypesWithAnnotations, lambda.ParameterRefKinds, lambda.RefKind); + var parameterEffectiveScopes = lambda.GetParameterEffectiveScopes(); + updatedLambda = boundLambda.CreateLambdaSymbol(updatedContaining, lambda.ReturnTypeWithAnnotations, lambda.ParameterTypesWithAnnotations, lambda.ParameterRefKinds, parameterEffectiveScopes, lambda.RefKind); } else { diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 47c9754a7cc6c..f3848cc9c4c90 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -154,12 +154,14 @@ internal LambdaSymbol CreateLambdaSymbol( TypeWithAnnotations returnType, ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes, RefKind refKind) => UnboundLambda.Data.CreateLambdaSymbol( containingSymbol, returnType, parameterTypes, parameterRefKinds.IsDefault ? Enumerable.Repeat(RefKind.None, parameterTypes.Length).ToImmutableArray() : parameterRefKinds, + parameterEffectiveScopes, refKind); /// @@ -347,7 +349,7 @@ public static void GetReturnTypes(ArrayBuilder<(BoundReturnStatement, TypeWithAn protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) @@ -611,7 +613,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo } var parameterRefKindsBuilder = ArrayBuilder.GetInstance(ParameterCount); - var parameterScopesBuilder = ArrayBuilder.GetInstance(ParameterCount); + var parameterEffectiveScopesBuilder = ArrayBuilder.GetInstance(ParameterCount); var parameterTypesBuilder = ArrayBuilder.GetInstance(ParameterCount); bool getEffectiveScopeFromSymbol = false; @@ -620,6 +622,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo var refKind = RefKind(i); var scope = DeclaredScope(i); var type = ParameterTypeWithAnnotations(i); + if (scope == DeclarationScope.Unscoped && ParameterHelpers.IsRefScopedByDefault(Binder.UseUpdatedEscapeRules, refKind)) { @@ -630,7 +633,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo } } parameterRefKindsBuilder.Add(refKind); - parameterScopesBuilder.Add(scope); + parameterEffectiveScopesBuilder.Add(scope); parameterTypesBuilder.Add(type); } var parameterRefKinds = parameterRefKindsBuilder.ToImmutableAndFree(); @@ -670,20 +673,18 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo for (int i = 0; i < ParameterCount; i++) { - if (DeclaredScope(i) == DeclarationScope.Unscoped && parameterScopesBuilder[i] == DeclarationScope.RefScoped && _unboundLambda.ParameterAttributes(i).Any()) + if (DeclaredScope(i) == DeclarationScope.Unscoped && parameterEffectiveScopesBuilder[i] == DeclarationScope.RefScoped && _unboundLambda.ParameterAttributes(i).Any()) { Debug.Assert(getEffectiveScopeFromSymbol); - parameterScopesBuilder[i] = lambdaSymbol.Parameters[i].EffectiveScope; + parameterEffectiveScopesBuilder[i] = lambdaSymbol.Parameters[i].EffectiveScope; } else { - Debug.Assert(lambdaSymbol.Parameters[i].EffectiveScope == parameterScopesBuilder[i]); + Debug.Assert(lambdaSymbol.Parameters[i].EffectiveScope == parameterEffectiveScopesBuilder[i]); } } } - var parameterScopes = parameterScopesBuilder.ToImmutableAndFree(); - if (!returnType.HasType) { // Binder.GetMethodGroupOrLambdaDelegateType() expects a non-null return type. @@ -695,7 +696,7 @@ private static TypeWithAnnotations DelegateReturnTypeWithAnnotations(MethodSymbo returnRefKind, returnType, parameterRefKinds, - parameterScopes, + parameterEffectiveScopesBuilder.ToImmutableAndFree(), parameterTypes); LambdaSymbol createLambdaSymbol() @@ -707,6 +708,7 @@ LambdaSymbol createLambdaSymbol() _unboundLambda, parameterTypes, parameterRefKinds, + parameterEffectiveScopesBuilder.ToImmutable(), refKind: default, returnType: default); } @@ -749,7 +751,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr } else { - lambdaSymbol = CreateLambdaSymbol(Binder.ContainingMemberOrLambda, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind); + lambdaSymbol = CreateLambdaSymbol(Binder.ContainingMemberOrLambda, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, cacheKey.ParameterEffectiveScopes, refKind); lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, GetWithParametersBinder(lambdaSymbol, Binder), inExpressionTree ? BinderFlags.InExpressionTree : BinderFlags.None); block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); } @@ -823,6 +825,7 @@ internal LambdaSymbol CreateLambdaSymbol( TypeWithAnnotations returnType, ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes, RefKind refKind) => new LambdaSymbol( Binder, @@ -831,6 +834,7 @@ internal LambdaSymbol CreateLambdaSymbol( _unboundLambda, parameterTypes, parameterRefKinds, + parameterEffectiveScopes, refKind, returnType); @@ -838,8 +842,10 @@ internal LambdaSymbol CreateLambdaSymbol(NamedTypeSymbol delegateType, Symbol co { var invokeMethod = DelegateInvokeMethod(delegateType); var returnType = DelegateReturnTypeWithAnnotations(invokeMethod, out RefKind refKind); - ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, out _); - return CreateLambdaSymbol(containingSymbol, returnType, parameterTypes, parameterRefKinds, refKind); + ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, + out var parameterEffectiveScopes, out _); + + return CreateLambdaSymbol(containingSymbol, returnType, parameterTypes, parameterRefKinds, parameterEffectiveScopes, refKind); } private void ValidateUnsafeParameters(BindingDiagnosticBag diagnostics, ImmutableArray targetParameterTypes) @@ -870,10 +876,11 @@ private void ValidateUnsafeParameters(BindingDiagnosticBag diagnostics, Immutabl private BoundLambda ReallyInferReturnType( NamedTypeSymbol? delegateType, ImmutableArray parameterTypes, - ImmutableArray parameterRefKinds) + ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes) { bool hasExplicitReturnType = HasExplicitReturnType(out var refKind, out var returnType); - (var lambdaSymbol, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, returnType, refKind); + (var lambdaSymbol, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, parameterEffectiveScopes, returnType, refKind); InferredLambdaReturnType inferredReturnType; if (hasExplicitReturnType) { @@ -928,6 +935,7 @@ private BoundLambda ReallyInferReturnType( private (LambdaSymbol lambdaSymbol, BoundBlock block, ExecutableCodeBinder lambdaBodyBinder, BindingDiagnosticBag diagnostics) BindWithParameterAndReturnType( ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes, TypeWithAnnotations returnType, RefKind refKind) { @@ -936,6 +944,7 @@ private BoundLambda ReallyInferReturnType( returnType, parameterTypes, parameterRefKinds, + parameterEffectiveScopes, refKind); var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, GetWithParametersBinder(lambdaSymbol, Binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); @@ -950,7 +959,7 @@ public BoundLambda BindForReturnTypeInference(NamedTypeSymbol delegateType) BoundLambda? result; if (!_returnInferenceCache!.TryGetValue(cacheKey, out result)) { - result = ReallyInferReturnType(delegateType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds); + result = ReallyInferReturnType(delegateType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, cacheKey.ParameterEffectiveScopes); result = ImmutableInterlocked.GetOrAdd(ref _returnInferenceCache, cacheKey, result); } @@ -964,16 +973,22 @@ private sealed class ReturnInferenceCacheKey { public readonly ImmutableArray ParameterTypes; public readonly ImmutableArray ParameterRefKinds; + public readonly ImmutableArray ParameterEffectiveScopes; public readonly NamedTypeSymbol? TaskLikeReturnTypeOpt; - public static readonly ReturnInferenceCacheKey Empty = new ReturnInferenceCacheKey(ImmutableArray.Empty, ImmutableArray.Empty, null); + public static readonly ReturnInferenceCacheKey Empty = new ReturnInferenceCacheKey(ImmutableArray.Empty, ImmutableArray.Empty, + parameterEffectiveScopes: default, taskLikeReturnTypeOpt: null); - private ReturnInferenceCacheKey(ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, NamedTypeSymbol? taskLikeReturnTypeOpt) + private ReturnInferenceCacheKey(ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes, NamedTypeSymbol? taskLikeReturnTypeOpt) { Debug.Assert(parameterTypes.Length == parameterRefKinds.Length); + Debug.Assert(parameterEffectiveScopes.IsDefault || parameterTypes.Length == parameterEffectiveScopes.Length); Debug.Assert(taskLikeReturnTypeOpt is null || ((object)taskLikeReturnTypeOpt == taskLikeReturnTypeOpt.ConstructedFrom && taskLikeReturnTypeOpt.IsCustomTaskType(out var builderArgument))); + this.ParameterTypes = parameterTypes; this.ParameterRefKinds = parameterRefKinds; + this.ParameterEffectiveScopes = parameterEffectiveScopes; this.TaskLikeReturnTypeOpt = taskLikeReturnTypeOpt; } @@ -1002,7 +1017,12 @@ public override bool Equals(object? obj) } } - return true; + if (this.ParameterEffectiveScopes.IsDefault || other.ParameterEffectiveScopes.IsDefault) + { + return this.ParameterEffectiveScopes.IsDefault == other.ParameterEffectiveScopes.IsDefault; + } + + return this.ParameterEffectiveScopes.SequenceEqual(other.ParameterEffectiveScopes); } public override int GetHashCode() @@ -1017,12 +1037,14 @@ public override int GetHashCode() public static ReturnInferenceCacheKey Create(NamedTypeSymbol? delegateType, bool isAsync) { - GetFields(delegateType, isAsync, out var parameterTypes, out var parameterRefKinds, out var taskLikeReturnTypeOpt); + GetFields(delegateType, isAsync, out var parameterTypes, out var parameterRefKinds, + out var parameterEffectiveScopes, out var taskLikeReturnTypeOpt); + if (parameterTypes.IsEmpty && parameterRefKinds.IsEmpty && taskLikeReturnTypeOpt is null) { return Empty; } - return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, taskLikeReturnTypeOpt); + return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, parameterEffectiveScopes, taskLikeReturnTypeOpt); } public static void GetFields( @@ -1030,12 +1052,14 @@ public static void GetFields( bool isAsync, out ImmutableArray parameterTypes, out ImmutableArray parameterRefKinds, + out ImmutableArray parameterEffectiveScopes, out NamedTypeSymbol? taskLikeReturnTypeOpt) { // delegateType or DelegateInvokeMethod can be null in cases of malformed delegates // in such case we would want something trivial with no parameters parameterTypes = ImmutableArray.Empty; parameterRefKinds = ImmutableArray.Empty; + parameterEffectiveScopes = default; taskLikeReturnTypeOpt = null; MethodSymbol? invoke = DelegateInvokeMethod(delegateType); if (invoke is not null) @@ -1054,6 +1078,7 @@ public static void GetFields( parameterTypes = typesBuilder.ToImmutableAndFree(); parameterRefKinds = refKindsBuilder.ToImmutableAndFree(); + parameterEffectiveScopes = invoke.GetParameterEffectiveScopes(); } if (isAsync) @@ -1120,16 +1145,19 @@ private BoundLambda ReallyBindForErrorRecovery() return GuessBestBoundLambda(_bindingCache!) ?? rebind(GuessBestBoundLambda(_returnInferenceCache!)) - ?? rebind(ReallyInferReturnType(delegateType: null, ImmutableArray.Empty, ImmutableArray.Empty)); + ?? rebind(ReallyInferReturnType(delegateType: null, ImmutableArray.Empty, ImmutableArray.Empty, parameterEffectiveScopes: default)); // Rebind a lambda to push target conversions through the return/result expressions [return: NotNullIfNotNull("lambda")] BoundLambda? rebind(BoundLambda? lambda) { if (lambda is null) return null; + var delegateType = (NamedTypeSymbol?)lambda.Type; - ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, out _); - return ReallyBindForErrorRecovery(delegateType, lambda.InferredReturnType, parameterTypes, parameterRefKinds); + ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, + out var parameterEffectiveScopes, taskLikeReturnTypeOpt: out _); + + return ReallyBindForErrorRecovery(delegateType, lambda.InferredReturnType, parameterTypes, parameterRefKinds, parameterEffectiveScopes); } } @@ -1137,7 +1165,8 @@ private BoundLambda ReallyBindForErrorRecovery( NamedTypeSymbol? delegateType, InferredLambdaReturnType inferredReturnType, ImmutableArray parameterTypes, - ImmutableArray parameterRefKinds) + ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes) { var returnType = inferredReturnType.TypeWithAnnotations; var refKind = inferredReturnType.RefKind; @@ -1156,7 +1185,7 @@ private BoundLambda ReallyBindForErrorRecovery( } } - (var lambdaSymbol, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, returnType, refKind); + (_, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, parameterEffectiveScopes, returnType, refKind); return new BoundLambda( _unboundLambda.Syntax, _unboundLambda, diff --git a/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs b/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs index 4bd6f77e53aaf..9032d4d2572da 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs @@ -63,6 +63,12 @@ internal BoundExpression SetInferredTypeWithAnnotations(TypeWithAnnotations type this.Syntax; Binder.CheckRestrictedTypeInAsyncMethod(localSymbol.ContainingSymbol, type.Type, diagnosticsOpt, typeOrDesignationSyntax); + + if (localSymbol.Scope == DeclarationScope.ValueScoped && !type.Type.IsErrorTypeOrRefLikeType()) + { + diagnosticsOpt.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, + (typeOrDesignationSyntax is TypeSyntax typeSyntax ? typeSyntax.SkipScoped(out _).SkipRef(out _) : typeOrDesignationSyntax).Location); + } } } @@ -120,7 +126,7 @@ private void ReportInferenceFailure(BindingDiagnosticBag diagnostics) designation = (SingleVariableDesignationSyntax)this.Syntax; break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Binder.Error( diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index dec462da992d5..616853f44bc14 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -985,7 +985,7 @@ Cannot call an abstract base member: '{0}' - A property or indexer may not be passed as an out or ref parameter + A non ref-returning property or indexer may not be used as an out or ref value Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}') @@ -5920,6 +5920,15 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ This ref-assigns a value that has a narrower escape scope than the target. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + enum generic type constraints @@ -7367,4 +7376,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'readonly' is not supported as a parameter modifier. Did you mean 'in'? + + The 'scoped' modifier cannot be used with discard. + + + A deconstruction variable cannot be declared as a ref local + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs index f584f98d56624..a893bcf3c9c5f 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs @@ -329,7 +329,7 @@ private void EmitDelegateCreation(BoundExpression node, BoundExpression receiver { if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } }) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } _builder.EmitOpCode(ILOpCode.Constrained); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 4efe4e8cada06..a83e4b624bb2f 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -48,7 +48,9 @@ private void EmitExpression(BoundExpression expression, bool used) return; } - if ((object)expression.Type == null || expression.Type.SpecialType != SpecialType.System_Decimal) + if ((object)expression.Type == null || + (expression.Type.SpecialType != SpecialType.System_Decimal && + !expression.Type.IsNullableType())) { EmitConstantExpression(expression.Type, constantValue, used, expression.Syntax); return; @@ -1552,7 +1554,7 @@ private void EmitStaticCallExpression(BoundCall call, UseKind useKind) { if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } }) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } _builder.EmitOpCode(ILOpCode.Constrained); @@ -3562,7 +3564,7 @@ private void EmitLoadFunction(BoundFunctionPointerLoad load, bool used) { if (load.ConstrainedToTypeOpt is not { TypeKind: TypeKind.TypeParameter }) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } _builder.EmitOpCode(ILOpCode.Constrained); diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index 2eeabc2182d66..d23a4ca17d506 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -534,7 +534,7 @@ private BoundExpression VisitExpressionCoreWithStackGuard(BoundExpression node, protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private void PushEvalStack(BoundExpression result, ExprContext context) @@ -2114,7 +2114,7 @@ public override BoundNode VisitCall(BoundCall node) } else if (receiverOpt is not null) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -2261,13 +2261,13 @@ public override RefKind RefKind /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable; + internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable(); /// /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; + internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable(); internal override DeclarationScope Scope => DeclarationScope.Unscoped; } 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/Compilation/AttributeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs index c6b787d323405..83583bdb01dd3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs @@ -16,40 +16,38 @@ namespace Microsoft.CodeAnalysis.CSharp internal sealed class AttributeSemanticModel : MemberSemanticModel { private readonly AliasSymbol _aliasOpt; + private readonly Symbol? _attributeTarget; - private AttributeSemanticModel( + internal AttributeSemanticModel( AttributeSyntax syntax, NamedTypeSymbol attributeType, + Symbol? attributeTarget, AliasSymbol aliasOpt, Binder rootBinder, - SyntaxTreeSemanticModel? containingSemanticModelOpt = null, - SyntaxTreeSemanticModel? parentSemanticModelOpt = null, - ImmutableDictionary? parentRemappedSymbolsOpt = null, - int speculatedPosition = 0) - : base(syntax, attributeType, new ExecutableCodeBinder(syntax, rootBinder.ContainingMember(), rootBinder), containingSemanticModelOpt, parentSemanticModelOpt, snapshotManagerOpt: null, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition) + PublicSemanticModel containingPublicSemanticModel, + ImmutableDictionary? parentRemappedSymbolsOpt = null) + : base(syntax, attributeType, new ExecutableCodeBinder(syntax, rootBinder.ContainingMember(), rootBinder), containingPublicSemanticModel, parentRemappedSymbolsOpt) { Debug.Assert(syntax != null); _aliasOpt = aliasOpt; + _attributeTarget = attributeTarget; } /// /// Creates an AttributeSemanticModel that allows asking semantic questions about an attribute node. /// - public static AttributeSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Symbol? attributeTarget, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt) + public static AttributeSemanticModel Create(PublicSemanticModel containingSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Symbol? attributeTarget, Binder rootBinder, ImmutableDictionary? parentRemappedSymbolsOpt) { rootBinder = attributeTarget is null ? rootBinder : new ContextualAttributeBinder(rootBinder, attributeTarget); - return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); + return new AttributeSemanticModel(syntax, attributeType, attributeTarget, aliasOpt, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); } /// /// Creates a speculative AttributeSemanticModel that allows asking semantic questions about an attribute node that did not appear in the original source code. /// - public static AttributeSemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt, int position) + public static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, AttributeSyntax syntax, NamedTypeSymbol attributeType, AliasSymbol aliasOpt, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); - return new AttributeSemanticModel(syntax, attributeType, aliasOpt, rootBinder, parentSemanticModelOpt: parentSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position); + return new SpeculativeSemanticModelWithMemberModel(parentSemanticModel, position, syntax, attributeType, aliasOpt, rootBinder, parentRemappedSymbolsOpt); } private NamedTypeSymbol AttributeType @@ -89,8 +87,7 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDi if (node.Kind() == SyntaxKind.Attribute) { var attribute = (AttributeSyntax)node; - // note: we should find the attributed member before binding the attribute as part of https://github.com/dotnet/roslyn/issues/53618 - return binder.BindAttribute(attribute, AttributeType, attributedMember: null, diagnostics); + return binder.BindAttribute(attribute, AttributeType, attributedMember: ContextualAttributeBinder.GetAttributedMember(_attributeTarget), diagnostics); } else if (SyntaxFacts.IsAttributeName(node)) { @@ -128,43 +125,43 @@ internal static bool IsNullableAnalysisEnabledIn(CSharpCompilation compilation, return compilation.IsNullableAnalysisEnabledIn(syntax); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel? speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel? speculativeModel) { speculativeModel = null; return false; diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 1e581738b374a..73b5d50ee2c50 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -2505,10 +2505,12 @@ public virtual DataFlowAnalysis AnalyzeDataFlow(StatementSyntax statement) public bool TryGetSpeculativeSemanticModelForMethodBody(int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(method); - return TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, method, out speculativeModel); + var result = TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, method, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a method body that did not appear in this source code. @@ -2530,10 +2532,12 @@ public bool TryGetSpeculativeSemanticModelForMethodBody(int position, BaseMethod public bool TryGetSpeculativeSemanticModelForMethodBody(int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(accessor); - return TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, accessor, out speculativeModel); + var result = TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, accessor, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a type syntax node that did not appear in @@ -2557,10 +2561,12 @@ public bool TryGetSpeculativeSemanticModelForMethodBody(int position, AccessorDe public bool TryGetSpeculativeSemanticModel(int position, TypeSyntax type, out SemanticModel speculativeModel, SpeculativeBindingOption bindingOption = SpeculativeBindingOption.BindAsExpression) { CheckModelAndSyntaxNodeToSpeculate(type); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, type, bindingOption, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, type, bindingOption, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a statement that did not appear in @@ -2581,10 +2587,12 @@ public bool TryGetSpeculativeSemanticModel(int position, TypeSyntax type, out Se public bool TryGetSpeculativeSemanticModel(int position, StatementSyntax statement, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(statement); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, statement, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, statement, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with an initializer that did not appear in @@ -2606,10 +2614,12 @@ public bool TryGetSpeculativeSemanticModel(int position, StatementSyntax stateme public bool TryGetSpeculativeSemanticModel(int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(initializer); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, initializer, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, initializer, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with an expression body that did not appear in @@ -2631,10 +2641,12 @@ public bool TryGetSpeculativeSemanticModel(int position, EqualsValueClauseSyntax public bool TryGetSpeculativeSemanticModel(int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(expressionBody); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, expressionBody, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, expressionBody, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in @@ -2659,10 +2671,12 @@ public bool TryGetSpeculativeSemanticModel(int position, ArrowExpressionClauseSy public bool TryGetSpeculativeSemanticModel(int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(constructorInitializer); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in @@ -2686,10 +2700,12 @@ public bool TryGetSpeculativeSemanticModel(int position, ConstructorInitializerS public bool TryGetSpeculativeSemanticModel(int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(constructorInitializer); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with a cref that did not appear in @@ -2714,10 +2730,12 @@ public bool TryGetSpeculativeSemanticModel(int position, PrimaryConstructorBaseT public bool TryGetSpeculativeSemanticModel(int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel) { CheckModelAndSyntaxNodeToSpeculate(crefSyntax); - return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, crefSyntax, out speculativeModel); + var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, crefSyntax, out PublicSemanticModel speculativeSyntaxTreeModel); + speculativeModel = speculativeSyntaxTreeModel; + return result; } - internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel); + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel); /// /// Get a SemanticModel object that is associated with an attribute that did not appear in diff --git a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs index 4ad1169eee1e5..c2ceb6136b97b 100644 --- a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs @@ -23,14 +23,12 @@ internal sealed class InitializerSemanticModel : MemberSemanticModel // create a SemanticModel for: // (a) A true field initializer (field = value) of a named type (incl. Enums) OR // (b) A parameter default value - private InitializerSemanticModel(CSharpSyntaxNode syntax, + internal InitializerSemanticModel(CSharpSyntaxNode syntax, Symbol symbol, Binder rootBinder, - SyntaxTreeSemanticModel containingSemanticModelOpt = null, - SyntaxTreeSemanticModel parentSemanticModelOpt = null, - ImmutableDictionary parentRemappedSymbolsOpt = null, - int speculatedPosition = 0) : - base(syntax, symbol, rootBinder, containingSemanticModelOpt, parentSemanticModelOpt, snapshotManagerOpt: null, parentRemappedSymbolsOpt, speculatedPosition) + PublicSemanticModel containingPublicSemanticModel, + ImmutableDictionary parentRemappedSymbolsOpt = null) : + base(syntax, symbol, rootBinder, containingPublicSemanticModel, parentRemappedSymbolsOpt) { Debug.Assert(!(syntax is ConstructorInitializerSyntax || syntax is PrimaryConstructorBaseTypeSyntax)); } @@ -58,7 +56,7 @@ internal static InitializerSemanticModel Create(SyntaxTreeSemanticModel containi /// /// Creates a SemanticModel for a parameter default value. /// - internal static InitializerSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, ParameterSyntax syntax, ParameterSymbol parameterSymbol, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt) + internal static InitializerSemanticModel Create(PublicSemanticModel containingSemanticModel, ParameterSyntax syntax, ParameterSymbol parameterSymbol, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt) { Debug.Assert(containingSemanticModel != null); return new InitializerSemanticModel(syntax, parameterSymbol, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); @@ -68,15 +66,9 @@ internal static InitializerSemanticModel Create(SyntaxTreeSemanticModel containi /// Creates a speculative SemanticModel for an initializer node (field initializer, constructor initializer, or parameter default value) /// that did not appear in the original source code. /// - internal static InitializerSemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, Symbol owner, CSharpSyntaxNode syntax, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt, int position) + internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, Symbol owner, EqualsValueClauseSyntax syntax, Binder rootBinder, ImmutableDictionary parentRemappedSymbolsOpt, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(syntax != null); - Debug.Assert(syntax.IsKind(SyntaxKind.EqualsValueClause)); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); - - return new InitializerSemanticModel(syntax, owner, rootBinder, parentSemanticModelOpt: parentSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position); + return new SpeculativeSemanticModelWithMemberModel(parentSemanticModel, position, owner, syntax, rootBinder, parentRemappedSymbolsOpt); } protected internal override CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNode node) @@ -208,7 +200,7 @@ private bool IsBindableInitializer(CSharpSyntaxNode node) } } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel) { var binder = this.GetEnclosingBinder(position); if (binder == null) @@ -222,37 +214,37 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return true; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs index b789c52336355..8536e0ef6a327 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs @@ -283,7 +283,7 @@ public override BoundNode VisitConstructorMethodBody(BoundConstructorMethodBody public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs index 0ecdcdc3f6b11..8d4ed467b8c22 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs @@ -18,21 +18,26 @@ internal abstract partial class MemberSemanticModel /// Allows asking semantic questions about a TypeSyntax (or its descendants) within a member, that did not appear in the original source code. /// Typically, an instance is obtained by a call to SemanticModel.TryGetSpeculativeSemanticModel. /// - private sealed class SpeculativeMemberSemanticModel : MemberSemanticModel + internal sealed class SpeculativeMemberSemanticModel : MemberSemanticModel { /// /// Creates a speculative SemanticModel for a TypeSyntax node at a position within an existing MemberSemanticModel. /// - public SpeculativeMemberSemanticModel(SyntaxTreeSemanticModel parentSemanticModel, Symbol owner, TypeSyntax root, Binder rootBinder, NullableWalker.SnapshotManager snapshotManagerOpt, ImmutableDictionary parentRemappedSymbolsOpt, int position) - : base(root, owner, rootBinder, containingSemanticModelOpt: null, parentSemanticModelOpt: parentSemanticModel, snapshotManagerOpt, parentRemappedSymbolsOpt, speculatedPosition: position) + public SpeculativeMemberSemanticModel( + PublicSemanticModel containingPublicSemanticModel, + Symbol owner, + TypeSyntax root, + Binder rootBinder, + ImmutableDictionary parentRemappedSymbolsOpt) + : base(root, owner, rootBinder, containingPublicSemanticModel: containingPublicSemanticModel, parentRemappedSymbolsOpt) { - Debug.Assert(parentSemanticModel is not null); + Debug.Assert(containingPublicSemanticModel is not null); } protected override NullableWalker.SnapshotManager GetSnapshotManager() { // In this override, current nullability state cannot influence anything of speculatively bound expressions. - return _parentSnapshotManagerOpt; + return ((SpeculativeSemanticModelWithMemberModel)_containingPublicSemanticModel).ParentSnapshotManagerOpt; } protected override BoundNode RewriteNullableBoundNodesWithSnapshots( @@ -54,42 +59,42 @@ protected override void AnalyzeBoundNodeNullability(BoundNode boundRoot, Binder protected override bool IsNullableAnalysisEnabled() { - return _parentSemanticModelOpt.IsNullableAnalysisEnabledAtSpeculativePosition(OriginalPositionForSpeculation, Root); + return ((SyntaxTreeSemanticModel)_containingPublicSemanticModel.ParentModel).IsNullableAnalysisEnabledAtSpeculativePosition(_containingPublicSemanticModel.OriginalPositionForSpeculation, Root); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index 15af834d57a1f..e46330abd63ad 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -20,6 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp { /// /// Binding info for expressions and statements that are part of a member declaration. + /// Instances of this class should not be exposed to external consumers. /// internal abstract partial class MemberSemanticModel : CSharpSemanticModel { @@ -33,21 +34,10 @@ internal abstract partial class MemberSemanticModel : CSharpSemanticModel private NullableWalker.SnapshotManager _lazySnapshotManager; private ImmutableDictionary _lazyRemappedSymbols; private readonly ImmutableDictionary _parentRemappedSymbolsOpt; - /// - /// Only used when this is a speculative semantic model. - /// - private readonly NullableWalker.SnapshotManager _parentSnapshotManagerOpt; internal readonly Binder RootBinder; - /// - /// Field specific to a non-speculative MemberSemanticModel that must have a containing semantic model. - /// - private readonly SyntaxTreeSemanticModel _containingSemanticModelOpt; - - // Fields specific to a speculative MemberSemanticModel. - private readonly SyntaxTreeSemanticModel _parentSemanticModelOpt; - private readonly int _speculatedPosition; + private readonly PublicSemanticModel _containingPublicSemanticModel; private readonly Lazy _operationFactory; @@ -55,29 +45,19 @@ protected MemberSemanticModel( CSharpSyntaxNode root, Symbol memberSymbol, Binder rootBinder, - SyntaxTreeSemanticModel containingSemanticModelOpt, - SyntaxTreeSemanticModel parentSemanticModelOpt, - NullableWalker.SnapshotManager snapshotManagerOpt, - ImmutableDictionary parentRemappedSymbolsOpt, - int speculatedPosition) + PublicSemanticModel containingPublicSemanticModel, + ImmutableDictionary parentRemappedSymbolsOpt) { Debug.Assert(root != null); Debug.Assert((object)memberSymbol != null); - Debug.Assert(parentSemanticModelOpt == null ^ containingSemanticModelOpt == null); - Debug.Assert(containingSemanticModelOpt == null || !containingSemanticModelOpt.IsSpeculativeSemanticModel); - Debug.Assert(parentSemanticModelOpt == null || !parentSemanticModelOpt.IsSpeculativeSemanticModel, CSharpResources.ChainingSpeculativeModelIsNotSupported); - Debug.Assert(snapshotManagerOpt == null || parentSemanticModelOpt != null); + Debug.Assert(containingPublicSemanticModel.IsSpeculativeSemanticModel == (containingPublicSemanticModel is SpeculativeSemanticModelWithMemberModel)); _root = root; _memberSymbol = memberSymbol; - - this.RootBinder = rootBinder.WithAdditionalFlags(GetSemanticModelBinderFlags()); - _containingSemanticModelOpt = containingSemanticModelOpt; - _parentSemanticModelOpt = parentSemanticModelOpt; - _parentSnapshotManagerOpt = snapshotManagerOpt; + _containingPublicSemanticModel = containingPublicSemanticModel; _parentRemappedSymbolsOpt = parentRemappedSymbolsOpt; - _speculatedPosition = speculatedPosition; + this.RootBinder = rootBinder.WithAdditionalFlags(GetSemanticModelBinderFlags()); _operationFactory = new Lazy(() => new CSharpOperationFactory(this)); } @@ -85,7 +65,7 @@ public override CSharpCompilation Compilation { get { - return (_containingSemanticModelOpt ?? _parentSemanticModelOpt).Compilation; + return _containingPublicSemanticModel.Compilation; } } @@ -112,7 +92,15 @@ public sealed override bool IsSpeculativeSemanticModel { get { - return _parentSemanticModelOpt != null; + return _containingPublicSemanticModel.IsSpeculativeSemanticModel; + } + } + + public sealed override bool IgnoresAccessibility + { + get + { + return _containingPublicSemanticModel.IgnoresAccessibility; } } @@ -120,7 +108,9 @@ public sealed override int OriginalPositionForSpeculation { get { - return _speculatedPosition; + // This property is not meaningful for member semantic models. + // An external consumer should never be able to access them directly. + throw ExceptionUtilities.Unreachable(); } } @@ -128,15 +118,17 @@ public sealed override CSharpSemanticModel ParentModel { get { - return _parentSemanticModelOpt; + // This property is not meaningful for member semantic models. + // An external consumer should never be able to access them directly. + throw ExceptionUtilities.Unreachable(); } } - internal sealed override SemanticModel ContainingModelOrSelf + internal sealed override SemanticModel ContainingPublicModelOrSelf { get { - return _containingSemanticModelOpt ?? (SemanticModel)this; + return _containingPublicSemanticModel; } } @@ -164,14 +156,14 @@ internal ImmutableDictionary GetRemappedSymbols() return _lazyRemappedSymbols; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel) { var expression = SyntaxFactory.GetStandaloneExpression(type); var binder = this.GetSpeculativeBinder(position, expression, bindingOption); if (binder != null) { - speculativeModel = new SpeculativeMemberSemanticModel(parentModel, _memberSymbol, type, binder, GetSnapshotManager(), GetRemappedSymbols(), position); + speculativeModel = new SpeculativeSemanticModelWithMemberModel(parentModel, position, _memberSymbol, type, binder, GetRemappedSymbols(), GetSnapshotManager()); return true; } @@ -179,7 +171,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman return false; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel) { // crefs can never legally appear within members. speculativeModel = null; @@ -566,22 +558,22 @@ private static BoundNode GetLowerBoundNode(ImmutableArray boundNodes) return boundNodes[boundNodes.Length - 1]; } - public override ImmutableArray GetSyntaxDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + public sealed override ImmutableArray GetSyntaxDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotSupportedException(); } - public override ImmutableArray GetDeclarationDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + public sealed override ImmutableArray GetDeclarationDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotSupportedException(); } - public override ImmutableArray GetMethodBodyDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + public sealed override ImmutableArray GetMethodBodyDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotSupportedException(); } - public override ImmutableArray GetDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + public sealed override ImmutableArray GetDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotSupportedException(); } @@ -1953,13 +1945,14 @@ protected void EnsureNullabilityAnalysisPerformedIfNecessary() // Not all speculative models are created with existing snapshots. Attributes, // TypeSyntaxes, and MethodBodies do not depend on existing state in a member, // and so the SnapshotManager can be null in these cases. - if (_parentSnapshotManagerOpt is null || !isNullableAnalysisEnabled) + var parentSnapshotManagerOpt = ((SpeculativeSemanticModelWithMemberModel)_containingPublicSemanticModel).ParentSnapshotManagerOpt; + if (parentSnapshotManagerOpt is null || !isNullableAnalysisEnabled) { rewriteAndCache(); return; } - boundRoot = NullableWalker.AnalyzeAndRewriteSpeculation(_speculatedPosition, boundRoot, binder, _parentSnapshotManagerOpt, out var newSnapshots, ref remappedSymbols); + boundRoot = NullableWalker.AnalyzeAndRewriteSpeculation(_containingPublicSemanticModel.OriginalPositionForSpeculation, boundRoot, binder, parentSnapshotManagerOpt, out var newSnapshots, ref remappedSymbols); GuardedAddBoundTreeForStandaloneSyntax(bindableRoot, boundRoot, newSnapshots, remappedSymbols); } else @@ -2331,12 +2324,12 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol) internal sealed override Func GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool ShouldSkipSyntaxNodeAnalysis(SyntaxNode node, ISymbol containingSymbol) + internal sealed override bool ShouldSkipSyntaxNodeAnalysis(SyntaxNode node, ISymbol containingSymbol) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs index eba9d23ee4424..d7abb6cebe7c9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs @@ -43,16 +43,13 @@ internal InitialState( } #nullable disable - private MethodBodySemanticModel( + internal MethodBodySemanticModel( MethodSymbol owner, Binder rootBinder, CSharpSyntaxNode syntax, - SyntaxTreeSemanticModel containingSemanticModelOpt = null, - SyntaxTreeSemanticModel parentSemanticModelOpt = null, - NullableWalker.SnapshotManager snapshotManagerOpt = null, - ImmutableDictionary parentRemappedSymbolsOpt = null, - int speculatedPosition = 0) - : base(syntax, owner, rootBinder, containingSemanticModelOpt, parentSemanticModelOpt, snapshotManagerOpt, parentRemappedSymbolsOpt, speculatedPosition) + PublicSemanticModel containingPublicSemanticModel, + ImmutableDictionary parentRemappedSymbolsOpt = null) + : base(syntax, owner, rootBinder, containingPublicSemanticModel, parentRemappedSymbolsOpt) { Debug.Assert((object)owner != null); Debug.Assert(owner.Kind == SymbolKind.Method); @@ -112,7 +109,7 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDi /// /// Creates a speculative SemanticModel for a method body that did not appear in the original source code. /// - internal static MethodBodySemanticModel CreateSpeculative( + internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative( SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, StatementSyntax syntax, @@ -121,60 +118,52 @@ internal static MethodBodySemanticModel CreateSpeculative( ImmutableDictionary parentRemappedSymbolsOpt, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(syntax != null); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); + return CreateSpeculativeForNode(parentSemanticModel, owner, syntax, rootBinder, snapshotManagerOpt, parentRemappedSymbolsOpt, position); + } - return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, snapshotManagerOpt: snapshotManagerOpt, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt, speculatedPosition: position); + private static SpeculativeSemanticModelWithMemberModel CreateSpeculativeForNode( + SyntaxTreeSemanticModel parentSemanticModel, + MethodSymbol owner, + CSharpSyntaxNode syntax, + Binder rootBinder, + NullableWalker.SnapshotManager snapshotManagerOpt, + ImmutableDictionary parentRemappedSymbolsOpt, + int position) + { + return new SpeculativeSemanticModelWithMemberModel(parentSemanticModel, position, owner, syntax, rootBinder, parentRemappedSymbolsOpt, snapshotManagerOpt); } /// /// Creates a speculative SemanticModel for an expression body that did not appear in the original source code. /// - internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, ArrowExpressionClauseSyntax syntax, Binder rootBinder, int position) + internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, ArrowExpressionClauseSyntax syntax, Binder rootBinder, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(syntax != null); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); - - return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, speculatedPosition: position); + return CreateSpeculativeForNode(parentSemanticModel, owner, syntax, rootBinder, snapshotManagerOpt: null, parentRemappedSymbolsOpt: null, position); } /// /// Creates a speculative SemanticModel for a constructor initializer that did not appear in the original source code. /// - internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, ConstructorInitializerSyntax syntax, Binder rootBinder, int position) + internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, ConstructorInitializerSyntax syntax, Binder rootBinder, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(syntax != null); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); - - return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, speculatedPosition: position); + return CreateSpeculativeForNode(parentSemanticModel, owner, syntax, rootBinder, snapshotManagerOpt: null, parentRemappedSymbolsOpt: null, position); } /// /// Creates a speculative SemanticModel for a constructor initializer that did not appear in the original source code. /// - internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, PrimaryConstructorBaseTypeSyntax syntax, Binder rootBinder, int position) + internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, PrimaryConstructorBaseTypeSyntax syntax, Binder rootBinder, int position) { - Debug.Assert(parentSemanticModel != null); - Debug.Assert(syntax != null); - Debug.Assert(rootBinder != null); - Debug.Assert(rootBinder.IsSemanticModelBinder); - - return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, speculatedPosition: position); + return CreateSpeculativeForNode(parentSemanticModel, owner, syntax, rootBinder, snapshotManagerOpt: null, parentRemappedSymbolsOpt: null, position); } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel) { // CONSIDER: Do we want to ensure that speculated method and the original method have identical signatures? return GetSpeculativeSemanticModelForMethodBody(parentModel, position, method.Body, out speculativeModel); } - private bool GetSpeculativeSemanticModelForMethodBody(SyntaxTreeSemanticModel parentModel, int position, BlockSyntax body, out SemanticModel speculativeModel) + private bool GetSpeculativeSemanticModelForMethodBody(SyntaxTreeSemanticModel parentModel, int position, BlockSyntax body, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -206,12 +195,12 @@ private bool GetSpeculativeSemanticModelForMethodBody(SyntaxTreeSemanticModel pa return true; } - internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel) { return GetSpeculativeSemanticModelForMethodBody(parentModel, position, accessor.Body, out speculativeModel); } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -229,7 +218,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return true; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -248,7 +237,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return true; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { if (MemberSymbol is MethodSymbol methodSymbol && methodSymbol.MethodKind == MethodKind.Constructor && Root.FindToken(position).Parent?.AncestorsAndSelf().OfType().FirstOrDefault()?.Parent == Root) @@ -267,7 +256,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { if (MemberSymbol is SynthesizedRecordConstructor primaryCtor && primaryCtor.GetSyntax() is RecordDeclarationSyntax recordDecl) @@ -290,7 +279,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel) { speculativeModel = null; return false; diff --git a/src/Compilers/CSharp/Portable/Compilation/PublicSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/PublicSemanticModel.cs new file mode 100644 index 0000000000000..a20b8224e5fd1 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Compilation/PublicSemanticModel.cs @@ -0,0 +1,53 @@ +// 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.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// Instances of this can be exposed to external consumers. + /// Other types of are not designed for direct exposure + /// and their implementation might not be able to handle external requests properly. + /// + internal abstract partial class PublicSemanticModel : CSharpSemanticModel + { + protected AttributeSemanticModel CreateModelForAttribute(Binder enclosingBinder, AttributeSyntax attribute, MemberSemanticModel containingModel) + { + AliasSymbol aliasOpt; + var attributeType = (NamedTypeSymbol)enclosingBinder.BindType(attribute.Name, BindingDiagnosticBag.Discarded, out aliasOpt).Type; + + // For attributes where a nameof could introduce some type parameters, we need to track the attribute target + Symbol? attributeTarget = getAttributeTarget(attribute.Parent?.Parent); + + return AttributeSemanticModel.Create( + this, + attribute, + attributeType, + aliasOpt, + attributeTarget, + enclosingBinder.WithAdditionalFlags(BinderFlags.AttributeArgument), + containingModel?.GetRemappedSymbols()); + + Symbol? getAttributeTarget(SyntaxNode? targetSyntax) + { + return targetSyntax switch + { + BaseMethodDeclarationSyntax or + LocalFunctionStatementSyntax or + ParameterSyntax or + TypeParameterSyntax or + IndexerDeclarationSyntax or + AccessorDeclarationSyntax or + DelegateDeclarationSyntax => GetDeclaredSymbolForNode(targetSyntax).GetSymbol(), + AnonymousFunctionExpressionSyntax anonymousFunction => GetSymbolInfo(anonymousFunction).Symbol.GetSymbol(), + _ => null + }; + } + } + + internal sealed override SemanticModel ContainingPublicModelOrSelf => this; + } +} diff --git a/src/Compilers/CSharp/Portable/Compilation/SpeculativeSemanticModelWithMemberModel.cs b/src/Compilers/CSharp/Portable/Compilation/SpeculativeSemanticModelWithMemberModel.cs new file mode 100644 index 0000000000000..2060d512bef7b --- /dev/null +++ b/src/Compilers/CSharp/Portable/Compilation/SpeculativeSemanticModelWithMemberModel.cs @@ -0,0 +1,566 @@ +// 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.Diagnostics; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// Instances of this type represent user-facing speculative semantic models that are backed by + /// internal . + /// + internal sealed class SpeculativeSemanticModelWithMemberModel : PublicSemanticModel + { + private readonly SyntaxTreeSemanticModel _parentSemanticModel; + private readonly int _position; + private readonly NullableWalker.SnapshotManager? _parentSnapshotManagerOpt; + private readonly MemberSemanticModel _memberModel; + private ImmutableDictionary _childMemberModels = ImmutableDictionary.Empty; + + private SpeculativeSemanticModelWithMemberModel( + SyntaxTreeSemanticModel parentSemanticModel, + int position, + NullableWalker.SnapshotManager? snapshotManagerOpt) + { + Debug.Assert(parentSemanticModel is not null); + + _parentSemanticModel = parentSemanticModel; + _position = position; + _parentSnapshotManagerOpt = snapshotManagerOpt; + _memberModel = null!; + } + + public SpeculativeSemanticModelWithMemberModel( + SyntaxTreeSemanticModel parentSemanticModel, + int position, + AttributeSyntax syntax, + NamedTypeSymbol attributeType, + AliasSymbol aliasOpt, + Binder rootBinder, + ImmutableDictionary? parentRemappedSymbolsOpt) + : this(parentSemanticModel, position, snapshotManagerOpt: null) + { + Debug.Assert(syntax != null); + Debug.Assert(rootBinder != null); + Debug.Assert(rootBinder.IsSemanticModelBinder); + + _memberModel = new AttributeSemanticModel(syntax, attributeType, getAttributeTargetFromPosition(position, parentSemanticModel), aliasOpt, rootBinder, containingPublicSemanticModel: this, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); + + static Symbol? getAttributeTargetFromPosition(int position, SyntaxTreeSemanticModel model) + { + var attributedNode = model.SyntaxTree.GetRoot().FindToken(position).Parent; + attributedNode = attributedNode?.FirstAncestorOrSelf()?.Parent; + + if (attributedNode is not null) + { + return model.GetDeclaredSymbolForNode(attributedNode).GetSymbol(); + } + + return null; + } + } + + public SpeculativeSemanticModelWithMemberModel( + SyntaxTreeSemanticModel parentSemanticModel, + int position, + Symbol owner, + EqualsValueClauseSyntax syntax, + Binder rootBinder, + ImmutableDictionary? parentRemappedSymbolsOpt) + : this(parentSemanticModel, position, snapshotManagerOpt: null) + { + Debug.Assert(syntax != null); + Debug.Assert(rootBinder != null); + Debug.Assert(rootBinder.IsSemanticModelBinder); + + _memberModel = new InitializerSemanticModel(syntax, owner, rootBinder, containingPublicSemanticModel: this, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); + } + + public SpeculativeSemanticModelWithMemberModel( + SyntaxTreeSemanticModel parentModel, + int position, + Symbol owner, + TypeSyntax type, + Binder rootBinder, + ImmutableDictionary? parentRemappedSymbolsOpt, + NullableWalker.SnapshotManager? snapshotManagerOpt) + : this(parentModel, position, snapshotManagerOpt) + { + _memberModel = new MemberSemanticModel.SpeculativeMemberSemanticModel(this, owner, type, rootBinder, parentRemappedSymbolsOpt); + } + + public SpeculativeSemanticModelWithMemberModel( + SyntaxTreeSemanticModel parentSemanticModel, + int position, + MethodSymbol owner, + CSharpSyntaxNode syntax, + Binder rootBinder, + ImmutableDictionary? parentRemappedSymbolsOpt, + NullableWalker.SnapshotManager? snapshotManagerOpt) + : this(parentSemanticModel, position, snapshotManagerOpt) + { + Debug.Assert(syntax != null); + Debug.Assert(rootBinder != null); + Debug.Assert(rootBinder.IsSemanticModelBinder); + + _memberModel = new MethodBodySemanticModel(owner, rootBinder, syntax, containingPublicSemanticModel: this, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt); + } + + internal NullableWalker.SnapshotManager? ParentSnapshotManagerOpt => _parentSnapshotManagerOpt; + + public override bool IsSpeculativeSemanticModel => true; + + public override int OriginalPositionForSpeculation => _position; + + public override CSharpSemanticModel ParentModel => _parentSemanticModel; + + public override CSharpCompilation Compilation => _parentSemanticModel.Compilation; + + internal override CSharpSyntaxNode Root => _memberModel.Root; + + public override SyntaxTree SyntaxTree => _memberModel.SyntaxTree; + + public override bool IgnoresAccessibility => _parentSemanticModel.IgnoresAccessibility; + + private MemberSemanticModel GetEnclosingMemberModel(int position) + { + AssertPositionAdjusted(position); + SyntaxNode? node = Root.FindTokenIncludingCrefAndNameAttributes(position).Parent; + return node is null ? _memberModel : GetEnclosingMemberModel(node); + } + + private MemberSemanticModel GetEnclosingMemberModel(SyntaxNode node) + { + if (node.SyntaxTree != SyntaxTree) + { + return _memberModel; + } + + var attributeOrParameter = node.FirstAncestorOrSelf(static n => n.Kind() is SyntaxKind.Attribute or SyntaxKind.Parameter); + + if (attributeOrParameter is null || + attributeOrParameter == Root || + attributeOrParameter.Parent is null || + !Root.Span.Contains(attributeOrParameter.Span)) + { + return _memberModel; + } + + MemberSemanticModel containing = GetEnclosingMemberModel(attributeOrParameter.Parent); + + switch (attributeOrParameter) + { + case AttributeSyntax attribute: + + return GetOrAddModelForAttribute(containing, attribute); + + case ParameterSyntax paramDecl: + + return GetOrAddModelForParameter(node, containing, paramDecl); + + default: + ExceptionUtilities.UnexpectedValue(attributeOrParameter); + return containing; + } + } + + private MemberSemanticModel GetOrAddModelForAttribute(MemberSemanticModel containing, AttributeSyntax attribute) + { + return ImmutableInterlocked.GetOrAdd(ref _childMemberModels, attribute, + (node, binderAndModel) => CreateModelForAttribute(binderAndModel.binder, (AttributeSyntax)node, binderAndModel.model), + (binder: containing.GetEnclosingBinder(attribute.SpanStart), model: containing)); + } + + private MemberSemanticModel GetOrAddModelForParameter(SyntaxNode node, MemberSemanticModel containing, ParameterSyntax paramDecl) + { + EqualsValueClauseSyntax? defaultValueSyntax = paramDecl.Default; + + if (defaultValueSyntax != null && defaultValueSyntax.FullSpan.Contains(node.Span)) + { + var parameterSymbol = containing.GetDeclaredSymbol(paramDecl).GetSymbol(); + if ((object)parameterSymbol != null) + { + return ImmutableInterlocked.GetOrAdd(ref _childMemberModels, defaultValueSyntax, + (equalsValue, tuple) => + InitializerSemanticModel.Create( + this, + tuple.paramDecl, + tuple.parameterSymbol, + tuple.containing.GetEnclosingBinder(tuple.paramDecl.SpanStart). + CreateBinderForParameterDefaultValue(tuple.parameterSymbol, + (EqualsValueClauseSyntax)equalsValue), + tuple.containing.GetRemappedSymbols()), + (compilation: this.Compilation, + paramDecl, + parameterSymbol, + containing) + ); + } + } + + return containing; + } + + internal override MemberSemanticModel GetMemberModel(SyntaxNode node) + { + return GetEnclosingMemberModel(node).GetMemberModel(node); + } + + public override Conversion ClassifyConversion( + ExpressionSyntax expression, + ITypeSymbol destination, + bool isExplicitInSource = false) + { + return GetEnclosingMemberModel(expression).ClassifyConversion(expression, destination, isExplicitInSource); + } + + internal override Conversion ClassifyConversionForCast( + ExpressionSyntax expression, + TypeSymbol destination) + { + return GetEnclosingMemberModel(expression).ClassifyConversionForCast(expression, destination); + } + + public override ImmutableArray GetSyntaxDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException(); + } + + public override ImmutableArray GetDeclarationDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException(); + } + + public override ImmutableArray GetMethodBodyDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException(); + } + + public override ImmutableArray GetDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException(); + } + + public override INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override INamedTypeSymbol GetDeclaredSymbol(DelegateDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IFieldSymbol GetDeclaredSymbol(EnumMemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(LocalFunctionStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(MemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IMethodSymbol GetDeclaredSymbol(CompilationUnitSyntax declarationSyntax, CancellationToken cancellationToken = default) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IMethodSymbol GetDeclaredSymbol(BaseMethodDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(BasePropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IPropertySymbol GetDeclaredSymbol(IndexerDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IEventSymbol GetDeclaredSymbol(EventDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IMethodSymbol GetDeclaredSymbol(AccessorDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IMethodSymbol GetDeclaredSymbol(ArrowExpressionClauseSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(VariableDeclaratorSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(SingleVariableDesignationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + internal override LocalSymbol GetAdjustedLocalSymbol(SourceLocalSymbol local) + { + return _memberModel.GetAdjustedLocalSymbol(local); + } + + public override ILabelSymbol GetDeclaredSymbol(LabeledStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override ILabelSymbol GetDeclaredSymbol(SwitchLabelSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IAliasSymbol GetDeclaredSymbol(UsingDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IAliasSymbol GetDeclaredSymbol(ExternAliasDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + public override IParameterSymbol GetDeclaredSymbol(ParameterSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbol(declarationSyntax, cancellationToken); + } + + internal override ImmutableArray GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declarationSyntax).GetDeclaredSymbols(declarationSyntax, cancellationToken); + } + + public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(typeParameter).GetDeclaredSymbol(typeParameter, cancellationToken); + } + + public override IRangeVariableSymbol GetDeclaredSymbol(JoinIntoClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetDeclaredSymbol(node, cancellationToken); + } + + public override IRangeVariableSymbol GetDeclaredSymbol(QueryClauseSyntax queryClause, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(queryClause).GetDeclaredSymbol(queryClause, cancellationToken); + } + + public override IRangeVariableSymbol GetDeclaredSymbol(QueryContinuationSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetDeclaredSymbol(node, cancellationToken); + } + + public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node) + { + return GetEnclosingMemberModel(node).GetAwaitExpressionInfo(node); + } + + public override ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node) + { + return GetEnclosingMemberModel(node).GetForEachStatementInfo(node); + } + + public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatementSyntax node) + { + return GetEnclosingMemberModel(node).GetForEachStatementInfo(node); + } + + public override DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node) + { + return GetEnclosingMemberModel(node).GetDeconstructionInfo(node); + } + + public override DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node) + { + return GetEnclosingMemberModel(node).GetDeconstructionInfo(node); + } + + public override QueryClauseInfo GetQueryClauseInfo(QueryClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetQueryClauseInfo(node, cancellationToken); + } + + public override IPropertySymbol GetDeclaredSymbol(AnonymousObjectMemberDeclaratorSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declaratorSyntax).GetDeclaredSymbol(declaratorSyntax, cancellationToken); + } + + public override INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declaratorSyntax).GetDeclaredSymbol(declaratorSyntax, cancellationToken); + } + + public override INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declaratorSyntax).GetDeclaredSymbol(declaratorSyntax, cancellationToken); + } + + public override ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(declaratorSyntax).GetDeclaredSymbol(declaratorSyntax, cancellationToken); + } + + internal override IOperation? GetOperationWorker(CSharpSyntaxNode node, CancellationToken cancellationToken) + { + return GetEnclosingMemberModel(node).GetOperationWorker(node, cancellationToken); + } + + internal override SymbolInfo GetSymbolInfoWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetSymbolInfoWorker(node, options, cancellationToken); + } + + internal override CSharpTypeInfo GetTypeInfoWorker(CSharpSyntaxNode node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetTypeInfoWorker(node, cancellationToken); + } + + internal override ImmutableArray GetMemberGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetMemberGroupWorker(node, options, cancellationToken); + } + + internal override ImmutableArray GetIndexerGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetIndexerGroupWorker(node, options, cancellationToken); + } + + internal override Optional GetConstantValueWorker(CSharpSyntaxNode node, CancellationToken cancellationToken) + { + return GetEnclosingMemberModel(node).GetConstantValueWorker(node, cancellationToken); + } + + internal override SymbolInfo GetCollectionInitializerSymbolInfoWorker(InitializerExpressionSyntax collectionInitializer, ExpressionSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(collectionInitializer).GetCollectionInitializerSymbolInfoWorker(collectionInitializer, node, cancellationToken); + } + + public override SymbolInfo GetSymbolInfo(OrderingSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetSymbolInfo(node, cancellationToken); + } + + public override SymbolInfo GetSymbolInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetSymbolInfo(node, cancellationToken); + } + + public override TypeInfo GetTypeInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + return GetEnclosingMemberModel(node).GetTypeInfo(node, cancellationToken); + } + + internal override Binder GetEnclosingBinderInternal(int position) + { + return GetEnclosingMemberModel(position).GetEnclosingBinderInternal(position); + } + + internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol) + { + return _memberModel.RemapSymbolIfNecessaryCore(symbol); + } + + internal sealed override Func GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool ShouldSkipSyntaxNodeAnalysis(SyntaxNode node, ISymbol containingSymbol) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics) + { + return GetEnclosingMemberModel(node).Bind(binder, node, diagnostics); + } + + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel? speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel) + { + throw ExceptionUtilities.Unreachable(); + } + + internal override BoundExpression GetSpeculativelyBoundExpression(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption, out Binder binder, out ImmutableArray crefSymbols) + { + return GetEnclosingMemberModel(CheckAndAdjustPosition(position)).GetSpeculativelyBoundExpression(position, expression, bindingOption, out binder, out crefSymbols); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Compilation/SpeculativeSyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SpeculativeSyntaxTreeSemanticModel.cs index 2d436857fc1f1..b421d5132cb8c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SpeculativeSyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SpeculativeSyntaxTreeSemanticModel.cs @@ -47,7 +47,7 @@ private static SpeculativeSyntaxTreeSemanticModel CreateCore(SyntaxTreeSemanticM } private SpeculativeSyntaxTreeSemanticModel(SyntaxTreeSemanticModel parentSemanticModel, CSharpSyntaxNode root, Binder rootBinder, int position, SpeculativeBindingOption bindingOption) - : base(parentSemanticModel.Compilation, parentSemanticModel.SyntaxTree, root.SyntaxTree) + : base(parentSemanticModel.Compilation, parentSemanticModel.SyntaxTree, root.SyntaxTree, parentSemanticModel.IgnoresAccessibility) { _parentSemanticModel = parentSemanticModel; _root = root; diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index d865e71511cbc..7ee9d300e4a3d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// /// Allows asking semantic questions about any node in a SyntaxTree within a Compilation. /// - internal partial class SyntaxTreeSemanticModel : CSharpSemanticModel + internal partial class SyntaxTreeSemanticModel : PublicSemanticModel { private readonly CSharpCompilation _compilation; private readonly SyntaxTree _syntaxTree; @@ -50,11 +50,12 @@ internal SyntaxTreeSemanticModel(CSharpCompilation compilation, SyntaxTree synta _binderFactory = compilation.GetBinderFactory(SyntaxTree, ignoreAccessibility); } - internal SyntaxTreeSemanticModel(CSharpCompilation parentCompilation, SyntaxTree parentSyntaxTree, SyntaxTree speculatedSyntaxTree) + internal SyntaxTreeSemanticModel(CSharpCompilation parentCompilation, SyntaxTree parentSyntaxTree, SyntaxTree speculatedSyntaxTree, bool ignoreAccessibility) { _compilation = parentCompilation; _syntaxTree = speculatedSyntaxTree; - _binderFactory = _compilation.GetBinderFactory(parentSyntaxTree); + _binderFactory = _compilation.GetBinderFactory(parentSyntaxTree, ignoreAccessibility); + _ignoresAccessibility = ignoreAccessibility; } /// @@ -366,7 +367,7 @@ private Symbol GetSemanticInfoSymbolInNonMemberContext(CSharpSyntaxNode node, bo // However, we can return fieldSymbol.Type for implicitly typed field symbols in both cases. // Note that for regular C#, fieldSymbol.Type would be an error type. - var variableDecl = type.Parent as VariableDeclarationSyntax; + var variableDecl = type.ModifyingScopedOrRefTypeOrSelf().Parent as VariableDeclarationSyntax; if (variableDecl != null && variableDecl.Variables.Any()) { var fieldSymbol = GetDeclaredFieldSymbol(variableDecl.Variables.First()); @@ -586,12 +587,7 @@ public override CSharpSemanticModel ParentModel get { return null; } } - internal override SemanticModel ContainingModelOrSelf - { - get { return this; } - } - - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -612,7 +608,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -627,7 +623,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -641,7 +637,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman return false; } - internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -655,7 +651,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(Sy return false; } - internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -669,7 +665,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelForMethodBodyCore(Sy return false; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -683,7 +679,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman return false; } - internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -697,7 +693,7 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -716,7 +712,7 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman return false; } - internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel) { position = CheckAndAdjustPosition(position); @@ -757,7 +753,7 @@ internal override BoundExpression GetSpeculativelyBoundExpression(int position, return GetSpeculativelyBoundExpressionWithoutNullability(position, expression, bindingOption, out binder, out crefSymbols); } - internal AttributeSemanticModel CreateSpeculativeAttributeSemanticModel(int position, AttributeSyntax attribute, Binder binder, AliasSymbol aliasOpt, NamedTypeSymbol attributeType) + internal PublicSemanticModel CreateSpeculativeAttributeSemanticModel(int position, AttributeSyntax attribute, Binder binder, AliasSymbol aliasOpt, NamedTypeSymbol attributeType) { var memberModel = IsNullableAnalysisEnabledAtSpeculativePosition(position, attribute) ? GetMemberModel(position) : null; return AttributeSemanticModel.CreateSpeculative(this, attribute, attributeType, aliasOpt, binder, memberModel?.GetRemappedSymbols(), position); @@ -1263,40 +1259,6 @@ private SynthesizedRecordConstructor TryGetSynthesizedRecordConstructor(RecordDe return symbol; } - private AttributeSemanticModel CreateModelForAttribute(Binder enclosingBinder, AttributeSyntax attribute, MemberSemanticModel containingModel) - { - AliasSymbol aliasOpt; - var attributeType = (NamedTypeSymbol)enclosingBinder.BindType(attribute.Name, BindingDiagnosticBag.Discarded, out aliasOpt).Type; - - // For attributes where a nameof could introduce some type parameters, we need to track the attribute target - Symbol attributeTarget = getAttributeTarget(attribute.Parent.Parent); - - return AttributeSemanticModel.Create( - this, - attribute, - attributeType, - aliasOpt, - attributeTarget, - enclosingBinder.WithAdditionalFlags(BinderFlags.AttributeArgument), - containingModel?.GetRemappedSymbols()); - - Symbol getAttributeTarget(SyntaxNode targetSyntax) - { - return targetSyntax switch - { - BaseMethodDeclarationSyntax or - LocalFunctionStatementSyntax or - ParameterSyntax or - TypeParameterSyntax or - IndexerDeclarationSyntax or - AccessorDeclarationSyntax or - DelegateDeclarationSyntax => GetDeclaredSymbolForNode(targetSyntax).GetSymbol(), - AnonymousFunctionExpressionSyntax anonymousFunction => GetSymbolInfo(anonymousFunction).Symbol.GetSymbol(), - _ => null - }; - } - } - private FieldSymbol GetDeclaredFieldSymbol(VariableDeclaratorSyntax variableDecl) { var declaredSymbol = GetDeclaredSymbol(variableDecl); diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel_RegionAnalysisContext.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel_RegionAnalysisContext.cs index a2e2436e2d8b0..3da52ceea31e0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel_RegionAnalysisContext.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel_RegionAnalysisContext.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// /// Allows asking semantic questions about any node in a SyntaxTree within a Compilation. /// - internal partial class SyntaxTreeSemanticModel : CSharpSemanticModel + internal partial class SyntaxTreeSemanticModel { private RegionAnalysisContext RegionAnalysisContext(ExpressionSyntax expression) { diff --git a/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs b/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs index 2c2e806fda60f..a28800e1942ba 100644 --- a/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs +++ b/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs @@ -212,7 +212,7 @@ private void VisitNamespaceMembersAsTasks(NamespaceSymbol symbol) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }), _cancellationToken)); } diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs index 9b05b5cb9998e..83b01dfb65155 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs @@ -291,7 +291,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, } } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } internal abstract partial class MethodToClassRewriter diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index e5f284179f780..e30d1f63cce93 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -379,7 +379,7 @@ private Task CompileNamespaceAsAsync(NamespaceSymbol symbol) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }), _cancellationToken); } @@ -425,7 +425,7 @@ private Task CompileNamedTypeAsync(NamedTypeSymbol symbol) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }), _cancellationToken); } @@ -903,22 +903,22 @@ private void CompileFieldLikeEventAccessor(SourceEventSymbol eventSymbol, bool i public override object VisitMethod(MethodSymbol symbol, TypeCompilationState arg) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override object VisitProperty(PropertySymbol symbol, TypeCompilationState argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override object VisitEvent(EventSymbol symbol, TypeCompilationState argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override object VisitField(FieldSymbol symbol, TypeCompilationState argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private void CompileMethod( diff --git a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs index 5509aef41b301..36c30da75eba5 100644 --- a/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/SynthesizedMetadataCompiler.cs @@ -107,7 +107,7 @@ public override void VisitProperty(PropertySymbol symbol) #if DEBUG public override void VisitMethod(MethodSymbol symbol) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endif } diff --git a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs index a96d2e0f74287..d4481c3211bd6 100644 --- a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs +++ b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs @@ -209,7 +209,7 @@ internal void ReportCtorInitializerCycles(MethodSymbol method1, MethodSymbol met if (method1 == method2) { // direct recursion is diagnosed elsewhere - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (_constructorInitializers == null) diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs index 07dc85c9f6e6e..60e1db19222b3 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs @@ -344,7 +344,7 @@ internal bool TryGetAnonymousTypeName(AnonymousTypeManager.AnonymousTypeTemplate public override Symbol DefaultVisit(Symbol symbol) { // Symbol should have been handled elsewhere. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Symbol? Visit(Symbol symbol) @@ -579,7 +579,7 @@ public override Symbol VisitDynamicType(DynamicTypeSymbol symbol) public override Symbol VisitParameter(ParameterSymbol parameter) { // Should never reach here. Should be matched as a result of matching the container. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Symbol? VisitPointerType(PointerTypeSymbol symbol) @@ -1044,7 +1044,7 @@ public DeepTranslator(NamedTypeSymbol systemObject) public override Symbol DefaultVisit(Symbol symbol) { // Symbol should have been handled elsewhere. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Symbol Visit(Symbol symbol) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs index 9980cd78e90b5..46fa3de3985c0 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs @@ -230,13 +230,13 @@ private static void Append(PooledStringBuilder result, object value) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/FunctionPointerTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/FunctionPointerTypeSymbolAdapter.cs index 009a4c2b1e895..08765ddc7a05d 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/FunctionPointerTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/FunctionPointerTypeSymbolAdapter.cs @@ -82,11 +82,11 @@ public ImmutableArray GetParameters(EmitContext conte public override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - public override int GetHashCode() => throw ExceptionUtilities.Unreachable; + public override int GetHashCode() => throw ExceptionUtilities.Unreachable(); public override string ToString() => _underlying.ToDisplayString(SymbolDisplayFormat.ILVisualizationFormat); } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs index cdd9f1466be0b..7001a361d209b 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/MethodSymbolAdapter.cs @@ -691,7 +691,7 @@ internal MethodSymbolAdapter(MethodSymbol underlyingMethodSymbol) if (underlyingMethodSymbol is NativeIntegerMethodSymbol) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs index cc3a0a331a047..da87c2a8b6779 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs @@ -168,13 +168,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index bfe4af8a0017b..4aafb3c441984 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -215,7 +215,7 @@ private Cci.ITypeDefinition AsTypeDefinitionImpl(PEModuleBuilder moduleBeingBuil void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); //We've not yet discovered a scenario in which we need this. //If you're hitting this exception. Uncomment the code below //and add a unit test. @@ -1044,7 +1044,7 @@ internal NamedTypeSymbolAdapter(NamedTypeSymbol underlyingNamedTypeSymbol) if (underlyingNamedTypeSymbol is NativeIntegerTypeSymbol) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PENetModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PENetModuleBuilder.cs index c17babbef5590..85b4e6fd9eea5 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PENetModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PENetModuleBuilder.cs @@ -27,12 +27,12 @@ internal PENetModuleBuilder( internal override SynthesizedAttributeData SynthesizeEmbeddedAttribute() { // Embedded attributes should never be synthesized in modules. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override void AddEmbeddedResourcesFromAddedModules(ArrayBuilder builder, DiagnosticBag diagnostics) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override EmitBaseline? PreviousGeneration => null; diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterSymbolAdapter.cs index 336a5e830119a..8f988d5ebd406 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterSymbolAdapter.cs @@ -162,7 +162,7 @@ ImmutableArray Cci.IParameterDefinition.MarshallingDescriptor void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); //At present we have no scenario that needs this method. //Should one arise, uncomment implementation and add a test. #if false @@ -272,7 +272,7 @@ internal ParameterSymbolAdapter(ParameterSymbol underlyingParameterSymbol) if (underlyingParameterSymbol is NativeIntegerParameterSymbol) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs index c1ac9b16ab3ff..973d8feaefb09 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs @@ -69,13 +69,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } @@ -120,13 +120,13 @@ ushort Cci.IParameterListEntry.Index public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs index e954cd866b4f4..37629be3f0e6f 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PropertySymbolAdapter.cs @@ -329,7 +329,7 @@ internal PropertySymbolAdapter(PropertySymbol underlyingPropertySymbol) if (underlyingPropertySymbol is NativeIntegerPropertySymbol) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs index 4a386fdd894c9..3f6099ccbc6b8 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs @@ -28,14 +28,14 @@ internal abstract partial class { Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } CodeAnalysis.Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => AdaptedSymbol; void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } IEnumerable Cci.IReference.GetAttributes(EmitContext context) @@ -48,7 +48,7 @@ internal partial class Symbol { #if DEBUG internal SymbolAdapter GetCciAdapter() => GetCciAdapterImpl(); - protected virtual SymbolAdapter GetCciAdapterImpl() => throw ExceptionUtilities.Unreachable; + protected virtual SymbolAdapter GetCciAdapterImpl() => throw ExceptionUtilities.Unreachable(); #else internal Symbol AdaptedSymbol => this; internal Symbol GetCciAdapter() => this; @@ -180,13 +180,13 @@ public sealed override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } [Conditional("DEBUG")] diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs index 4efb7dd6d012c..a6fccdc4eaa3d 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs @@ -53,13 +53,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs index 1cd67e481413d..fe3beb724f9bf 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs @@ -150,7 +150,7 @@ Cci.ITypeDefinition Cci.ITypeReference.AsTypeDefinition(EmitContext context) void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); //We've not yet discovered a scenario in which we need this. //If you're hitting this exception, uncomment the code below //and add a unit test. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 67a38cd339ae9..4b71bce270a8f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2112,7 +2112,7 @@ internal enum ErrorCode ERR_FeatureNotAvailableInVersion11 = 9058, ERR_RefFieldInNonRefStruct = 9059, ERR_CannotMatchOnINumberBase = 9060, - // Available 9061, + ERR_ScopedDiscard = 9061, ERR_ScopedTypeNameDisallowed = 9062, ERR_UnscopedRefAttributeUnsupportedTarget = 9063, ERR_RuntimeDoesNotSupportRefFields = 9064, @@ -2123,7 +2123,7 @@ internal enum ErrorCode ERR_FilePathCannotBeConvertedToUtf8 = 9069, ERR_ReadOnlyNotSuppAsParamModDidYouMeanIn = 9070, ERR_FileLocalDuplicateNameInNS = 9071, - // Available 9072, + ERR_DeconstructVariableCannotBeByRef = 9072, WRN_ScopedMismatchInParameterOfTarget = 9073, WRN_ScopedMismatchInParameterOfOverrideOrImplementation = 9074, ERR_RefReturnScopedParameter = 9075, @@ -2148,6 +2148,8 @@ internal enum ErrorCode WRN_RefAssignReturnOnly = 9093, WRN_RefReturnOnlyParameter = 9094, WRN_RefReturnOnlyParameter2 = 9095, + ERR_RefAssignValEscapeWider = 9096, + WRN_RefAssignValEscapeWider = 9097, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 26b501768be40..22e689685f20d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -521,6 +521,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_RefAssignReturnOnly: case ErrorCode.WRN_RefReturnOnlyParameter: case ErrorCode.WRN_RefReturnOnlyParameter2: + case ErrorCode.WRN_RefAssignValEscapeWider: return 1; default: return 0; @@ -1819,6 +1820,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_InvalidObjectCreation: case ErrorCode.WRN_TypeParameterSameAsOuterMethodTypeParameter: case ErrorCode.ERR_OutVariableCannotBeByRef: + case ErrorCode.ERR_DeconstructVariableCannotBeByRef: case ErrorCode.ERR_OmittedTypeArgument: case ErrorCode.ERR_FeatureNotAvailableInVersion8: case ErrorCode.ERR_AltInterpolatedVerbatimStringsNotAvailable: @@ -2218,6 +2220,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_BadAbstractEqualityOperatorSignature: case ErrorCode.ERR_BadBinaryReadOnlySpanConcatenation: case ErrorCode.ERR_ScopedRefAndRefStructOnly: + case ErrorCode.ERR_ScopedDiscard: case ErrorCode.ERR_FixedFieldMustNotBeRef: case ErrorCode.ERR_RefFieldCannotReferToRefStruct: case ErrorCode.ERR_FileTypeDisallowedInSignature: @@ -2263,6 +2266,8 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.WRN_RefAssignReturnOnly: case ErrorCode.WRN_RefReturnOnlyParameter: case ErrorCode.WRN_RefReturnOnlyParameter2: + case ErrorCode.ERR_RefAssignValEscapeWider: + case ErrorCode.WRN_RefAssignValEscapeWider: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 0a0b728b142de..763f55f12b794 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1058,7 +1058,7 @@ public virtual void VisitPattern(BoundPattern pattern) public override BoundNode VisitConstantPattern(BoundConstantPattern node) { // All patterns are handled by VisitPattern - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) @@ -1981,7 +1981,7 @@ public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstruct public sealed override BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructVarPendingInference node) { // OutDeconstructVarPendingInference nodes are only used within initial binding, but don't survive past that stage - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) @@ -3239,7 +3239,7 @@ public override BoundNode VisitDefaultExpression(BoundDefaultExpression node) public override BoundNode VisitUnconvertedObjectCreationExpression(BoundUnconvertedObjectCreationExpression node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node) @@ -3457,12 +3457,12 @@ public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlac public sealed override BoundNode VisitOutVariablePendingInference(OutVariablePendingInference node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public sealed override BoundNode VisitDeconstructionVariablePendingInference(DeconstructionVariablePendingInference node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitDiscardExpression(BoundDiscardExpression node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs index 1f30a384dc3e3..b1144ce207535 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs @@ -87,19 +87,19 @@ public bool Equals(VariableIdentifier other) public override bool Equals(object? obj) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } [Obsolete] public static bool operator ==(VariableIdentifier left, VariableIdentifier right) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } [Obsolete] public static bool operator !=(VariableIdentifier left, VariableIdentifier right) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override string ToString() diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index ab95751b3e47e..8f3cc98f50137 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -1685,6 +1685,23 @@ protected override void EnterParameter(ParameterSymbol parameter) if (slot > 0) SetSlotState(slot, true); NoteWrite(parameter, value: null, read: true); } + + if (parameter is SourceComplexParameterSymbolBase { ContainingSymbol: LocalFunctionSymbol or LambdaSymbol } sourceComplexParam && + sourceComplexParam.BindParameterAttributes() is { IsDefaultOrEmpty: false } boundAttributes) + { + // Mark attribute arguments as used. + foreach (var boundAttribute in boundAttributes) + { + foreach (var attributeArgument in boundAttribute.ConstructorArguments) + { + VisitRvalue(attributeArgument); + } + foreach (var attributeNamedArgumentAssignment in boundAttribute.NamedArguments) + { + VisitRvalue(attributeNamedArgumentAssignment.Right); + } + } + } } protected override void LeaveParameters(ImmutableArray parameters, SyntaxNode syntax, Location location) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs index 60a90df5dccab..d2193ab38cc6a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs @@ -50,18 +50,18 @@ public override bool Equals(Symbol obj, TypeCompareKind compareKind) public override ImmutableArray Locations => ImmutableArray.Empty; public override TypeWithAnnotations TypeWithAnnotations => _type; internal override LocalDeclarationKind DeclarationKind => LocalDeclarationKind.None; - internal override SyntaxToken IdentifierToken => throw ExceptionUtilities.Unreachable; + internal override SyntaxToken IdentifierToken => throw ExceptionUtilities.Unreachable(); internal override bool IsCompilerGenerated => true; internal override bool IsImportedFromMetadata => false; internal override bool IsPinned => false; public override RefKind RefKind => RefKind.None; - internal override SynthesizedLocalKind SynthesizedKind => throw ExceptionUtilities.Unreachable; + internal override SynthesizedLocalKind SynthesizedKind => throw ExceptionUtilities.Unreachable(); internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, BindingDiagnosticBag diagnostics = null) => null; internal override ImmutableBindingDiagnostic GetConstantValueDiagnostics(BoundExpression boundInitValue) => ImmutableBindingDiagnostic.Empty; - internal override SyntaxNode GetDeclaratorSyntax() => throw ExceptionUtilities.Unreachable; - internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) => throw ExceptionUtilities.Unreachable; - internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable; - internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; + internal override SyntaxNode GetDeclaratorSyntax() => throw ExceptionUtilities.Unreachable(); + internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) => throw ExceptionUtilities.Unreachable(); + internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable(); + internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable(); internal override DeclarationScope Scope => DeclarationScope.Unscoped; } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 1b72d2b0b0e39..a4b6ed41c5ec3 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -975,7 +975,7 @@ static ImmutableArray membersToBeInitialized(NamedTypeSymbol containingT => getAllTypeAndRequiredMembers(containingType), (includeAllMembers: _, includeCurrentTypeRequiredMembers: false, includeBaseRequiredMembers: true) - => throw ExceptionUtilities.Unreachable, + => throw ExceptionUtilities.Unreachable(), }; static ImmutableArray getAllTypeAndRequiredMembers(TypeSymbol containingType) @@ -2846,6 +2846,17 @@ private bool TryGetReturnType(out TypeWithAnnotations type, out FlowAnalysisAnno public override BoundNode? VisitLocal(BoundLocal node) { var local = node.LocalSymbol; + + // Ignore var self-references (e.g., the RHS of `var x = x;`) to avoid cycles. + // While inferring the type of a more complex construct (like lambda), + // nullability analysis could be triggered against a reference of the local being inferred, + // querying its type and hence starting the same type inference recursively. + if (local is SourceLocalSymbol { IsVar: true } && local.ForbiddenZone?.Contains(node.Syntax) == true) + { + SetResultType(node, TypeWithState.ForType(node.Type)); + return null; + } + int slot = GetOrCreateSlot(local); var type = GetDeclaredLocalResult(local); @@ -3051,7 +3062,7 @@ protected override void VisitLocalFunctionUse( bool isCall) { // Do not use this overload in NullableWalker. Use the overload below instead. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private void VisitLocalFunctionUse(LocalFunctionSymbol symbol) @@ -3794,7 +3805,7 @@ conversionCompletion is not null ? private new void VisitCollectionElementInitializer(BoundCollectionElementInitializer node) #pragma warning restore IDE0051 // Remove unused private members { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private Action? VisitCollectionElementInitializer(BoundCollectionElementInitializer node, TypeSymbol containingType, bool delayCompletionForType) @@ -6004,7 +6015,7 @@ private static bool HasImplicitTypeArguments(SyntaxNode syntax) protected override void VisitArguments(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method) { // Callers should be using VisitArguments overload below. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private (MethodSymbol? method, ImmutableArray results, bool returnNotNull) VisitArguments( @@ -9431,7 +9442,7 @@ private ImmutableArray GetDeconstructionRightParts(BoundExpress return fields.SelectAsArray((f, e) => (BoundExpression)new BoundFieldAccess(e.Syntax, e, f, constantValueOpt: null), expr); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitIncrementOperator(BoundIncrementOperator node) @@ -10157,7 +10168,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) public override BoundNode? VisitObjectInitializerMember(BoundObjectInitializerMember node) { // Should be handled by VisitObjectCreationExpression. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node) @@ -10735,7 +10746,7 @@ private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpress public override BoundNode? VisitAnonymousPropertyDeclaration(BoundAnonymousPropertyDeclaration node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitNoPiaObjectCreationExpression(BoundNoPiaObjectCreationExpression node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index ada9d0a5437fb..7cbb11cf984ad 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -384,7 +384,7 @@ public PossiblyConditionalState Clone() case BoundEvaluationDecisionDagNode p: { var evaluation = p.Evaluation; - (int inputSlot, TypeSymbol inputType) = tempMap.TryGetValue(evaluation.Input, out var slotAndType) ? slotAndType : throw ExceptionUtilities.Unreachable; + (int inputSlot, TypeSymbol inputType) = tempMap.TryGetValue(evaluation.Input, out var slotAndType) ? slotAndType : throw ExceptionUtilities.Unreachable(); Debug.Assert(inputSlot > 0); switch (evaluation) diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index edf0a4c1cd2bf..0ae9a81699ddf 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -309,6 +309,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_RefAssignReturnOnly: case ErrorCode.WRN_RefReturnOnlyParameter: case ErrorCode.WRN_RefReturnOnlyParameter2: + case ErrorCode.WRN_RefAssignValEscapeWider: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs index d9767dbac8ba1..afd7a831f64cb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs @@ -321,7 +321,7 @@ public sealed override BoundNode VisitExpressionStatement(BoundExpressionStateme public sealed override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { // await expressions must, by now, have been moved to the top level. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public sealed override BoundNode VisitBadExpression(BoundBadExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.Tree.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.Tree.cs index 8e4e2d17c9e35..9a17f5829b70f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.Tree.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.Tree.cs @@ -393,7 +393,7 @@ private void Build() } public override BoundNode VisitMethodGroup(BoundMethodGroup node) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override BoundNode VisitBlock(BoundBlock node) { diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.cs index 4ddfac42b5693..5ea98985fcf10 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.Analysis.cs @@ -187,7 +187,7 @@ private void ComputeLambdaScopesAndFrameCaptures() if (capturedEnvs.Count > 0) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } capturedEnvs.Free(); @@ -401,7 +401,7 @@ private PooledDictionary> CalculateFunction if (currentScope == null) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (currentScope.DeclaredEnvironment is null || @@ -601,7 +601,7 @@ public static Scope GetScopeParent(Scope treeRoot, BoundNode scopeNode) /// public static Scope GetScopeWithMatchingBoundNode(Scope treeRoot, BoundNode node) { - return Helper(treeRoot) ?? throw ExceptionUtilities.Unreachable; + return Helper(treeRoot) ?? throw ExceptionUtilities.Unreachable(); Scope Helper(Scope currentScope) { @@ -643,7 +643,7 @@ public static (NestedFunction, Scope) GetVisibleNestedFunction(Scope startingSco } currentScope = currentScope.Parent; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// @@ -651,7 +651,7 @@ public static (NestedFunction, Scope) GetVisibleNestedFunction(Scope startingSco /// public static NestedFunction GetNestedFunctionInTree(Scope treeRoot, MethodSymbol functionSymbol) { - return helper(treeRoot) ?? throw ExceptionUtilities.Unreachable; + return helper(treeRoot) ?? throw ExceptionUtilities.Unreachable(); NestedFunction helper(Scope scope) { diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 2600e1c6e4782..56dd8628b0aa8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -1732,7 +1732,7 @@ private static bool InLoopOrLambda(SyntaxNode lambdaSyntax, SyntaxNode scopeSynt public override BoundNode VisitLambda(BoundLambda node) { // these nodes have been handled in the context of the enclosing anonymous method conversion. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endregion diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs index 93d78aeb2f272..efbfc57120339 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs @@ -104,7 +104,7 @@ public override DllImportData GetDllImportData() internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation @@ -256,6 +256,6 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l return _stateMachineType.KickoffMethod.CalculateLocalSyntaxOffset(localPosition, localTree); } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 2c7d98ac45d9c..3472bb4419165 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -39,7 +39,7 @@ internal DelegateCacheContainer(MethodSymbol ownerMethod, int topLevelMethodOrdi public override Symbol ContainingSymbol => _containingSymbol; - public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable; + public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable(); public override TypeKind TypeKind => TypeKind.Class; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs index 982c07056192a..70231c91adda7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs @@ -33,7 +33,7 @@ public override TypeKind TypeKind public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsRecord => false; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index d16fb85e633ae..a58443b1efc36 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -374,12 +374,12 @@ private static Instrumenter RemoveDynamicAnalysisInjectors(Instrumenter instrume public override BoundNode VisitDefaultLiteral(BoundDefaultLiteral node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitUnconvertedObjectCreationExpression(BoundUnconvertedObjectCreationExpression node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitValuePlaceholder(BoundValuePlaceholder node) @@ -471,13 +471,13 @@ private void RemovePlaceholderReplacement(BoundValuePlaceholderBase placeholder) public sealed override BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructVarPendingInference node) { // OutDeconstructVarPendingInference nodes are only used within initial binding, but don't survive past that stage - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitDeconstructionVariablePendingInference(DeconstructionVariablePendingInference node) { // DeconstructionVariablePendingInference nodes are only used within initial binding, but don't survive past that stage - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitBadExpression(BoundBadExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 654c0671d6cf6..999a93749d0e9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -131,7 +131,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio // - Assignment operation is not supported for custom (non-field like) events. // - Access to regular field-like events is expected to be lowered to at least a field access // when we reach here. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); default: return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, type: type, used: used); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ConditionalAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ConditionalAccess.cs index 49ae9037dbcb6..7229bd81d2de2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ConditionalAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ConditionalAccess.cs @@ -18,7 +18,7 @@ public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // null when currently enclosing conditional access node diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 16b88c3744327..75febef3f24c6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -1489,7 +1489,7 @@ public static SpecialMember GetIntPtrConversionMethod(TypeSymbol source, TypeSym } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // https://github.com/dotnet/roslyn/issues/42452: Test with native integers and expression trees. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs index 7be13c0d14c32..ad53fa03e6fa0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs @@ -228,7 +228,7 @@ private ImmutableArray GetRightParts(BoundExpression right, Con return AccessTupleFields(VisitExpression(right), temps, effects.deconstructions); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static bool IsTupleExpression(BoundKind kind) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs index 8b7297012465b..f168a05abfce0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs @@ -188,7 +188,7 @@ private bool HasGotoOut(BoundNode node) public override BoundNode VisitFixedLocalCollectionInitializer(BoundFixedLocalCollectionInitializer node) { - throw ExceptionUtilities.Unreachable; //Should be handled by VisitFixedStatement + throw ExceptionUtilities.Unreachable(); //Should be handled by VisitFixedStatement } private BoundStatement InitializeFixedStatementLocal( diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index bb62966ed2f13..8fe8b52c0d5a8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -165,22 +165,22 @@ private BoundExpression MakeIndexerAccess( public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LocalDeclaration.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LocalDeclaration.cs index 3423f31e0ba6d..0f10fca3be353 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LocalDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LocalDeclaration.cs @@ -86,7 +86,7 @@ private BoundStatement InstrumentLocalDeclarationIfNecessary(BoundLocalDeclarati public sealed override BoundNode VisitOutVariablePendingInference(OutVariablePendingInference node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleCreationExpression.cs index 203e7c2301751..3124adf61698d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_TupleCreationExpression.cs @@ -14,7 +14,7 @@ internal partial class LocalRewriter { public override BoundNode VisitTupleLiteral(BoundTupleLiteral node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitConvertedTupleLiteral(BoundConvertedTupleLiteral node) diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index 764a96902242f..190cee288c829 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -454,14 +454,14 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol)); Debug.Assert(originalRight.Kind != BoundKind.ConvertedStackAllocExpression); //spilling ref local variables - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (NeedsProxy(leftLocal.LocalSymbol) && !proxies.ContainsKey(leftLocal.LocalSymbol)) { Debug.Assert(leftLocal.LocalSymbol.DeclarationKind == LocalDeclarationKind.None); // spilling temp variables - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } BoundExpression rewrittenLeft = (BoundExpression)this.Visit(leftLocal); @@ -601,7 +601,7 @@ public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalA } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } else { @@ -644,7 +644,7 @@ public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalA } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private FieldSymbol VisitFieldSymbol(FieldSymbol field) diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 9dbb1e415ff37..c1358c00aad2e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -807,7 +807,7 @@ public override BoundNode VisitBadExpression(BoundBadExpression node) public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) @@ -976,7 +976,7 @@ public override BoundNode VisitPassByCopy(BoundPassByCopy node) public override BoundNode VisitMethodGroup(BoundMethodGroup node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 42c94e552ff81..ae5a282273037 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -756,12 +756,12 @@ public override BoundNode VisitScope(BoundScope node) public override BoundNode VisitForStatement(BoundForStatement node) { - throw ExceptionUtilities.Unreachable; // for statements have been lowered away by now + throw ExceptionUtilities.Unreachable(); // for statements have been lowered away by now } public override BoundNode VisitUsingStatement(BoundUsingStatement node) { - throw ExceptionUtilities.Unreachable; // using statements have been lowered away by now + throw ExceptionUtilities.Unreachable(); // using statements have been lowered away by now } public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 39a43fefda05e..4865ca8408253 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -280,8 +279,9 @@ public CSharpOperationFactory(SemanticModel semanticModel) return CreateBoundInterpolatedStringArgumentPlaceholder((BoundInterpolatedStringArgumentPlaceholder)boundNode); case BoundKind.InterpolatedStringHandlerPlaceholder: return CreateBoundInterpolatedStringHandlerPlaceholder((BoundInterpolatedStringHandlerPlaceholder)boundNode); - case BoundKind.Attribute: + return CreateBoundAttributeOperation((BoundAttribute)boundNode); + case BoundKind.ArgList: case BoundKind.ArgListOperator: case BoundKind.ConvertedStackAllocExpression: @@ -493,6 +493,27 @@ private IOperation CreateBoundUnconvertedAddressOfOperatorOperation(BoundUnconve boundUnconvertedAddressOf.WasCompilerGenerated); } + private IOperation CreateBoundAttributeOperation(BoundAttribute boundAttribute) + { + var isAttributeImplicit = boundAttribute.WasCompilerGenerated; + if (boundAttribute.Constructor is null) + { + var invalidOperation = OperationFactory.CreateInvalidOperation(_semanticModel, boundAttribute.Syntax, GetIOperationChildren(boundAttribute), isImplicit: true); + return new AttributeOperation(invalidOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit); + } + + ObjectOrCollectionInitializerOperation? initializer = null; + if (!boundAttribute.NamedArguments.IsEmpty) + { + var namedArguments = CreateFromArray(boundAttribute.NamedArguments); + initializer = new ObjectOrCollectionInitializerOperation(namedArguments, _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), isImplicit: true); + Debug.Assert(initializer.Initializers.All(i => i is ISimpleAssignmentOperation)); + } + + var objectCreationOperation = new ObjectCreationOperation(boundAttribute.Constructor.GetPublicSymbol(), initializer, DeriveArguments(boundAttribute), _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), boundAttribute.ConstantValue, isImplicit: true); + return new AttributeOperation(objectCreationOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit); + } + internal ImmutableArray CreateIgnoredDimensions(BoundNode declaration, SyntaxNode declarationSyntax) { switch (declaration.Kind) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs index fd1b944398b31..7ed9a5fcb2604 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs @@ -37,7 +37,7 @@ internal IArgumentOperation CreateArgumentOperation(ArgumentKind kind, IParamete { // put argument syntax to argument operation IOperation value = Create(expression); - (SyntaxNode syntax, bool isImplicit) = expression.Syntax is { Parent: ArgumentSyntax parent } ? (parent, expression.WasCompilerGenerated) : (value.Syntax, true); + (SyntaxNode syntax, bool isImplicit) = expression.Syntax is { Parent: ArgumentSyntax or AttributeArgumentSyntax } ? (expression.Syntax.Parent, expression.WasCompilerGenerated) : (value.Syntax, true); return new ArgumentOperation( kind, parameter, @@ -238,6 +238,15 @@ internal ImmutableArray DeriveArguments(BoundNode containing objectCreation.Expanded, objectCreation.Syntax); } + case BoundKind.Attribute: + var attribute = (BoundAttribute)containingExpression; + Debug.Assert(attribute.Constructor is not null); + return DeriveArguments(attribute.Constructor, + attribute.ConstructorArguments, + attribute.ConstructorArgumentsToParamsOpt, + attribute.ConstructorDefaultArguments, + attribute.ConstructorExpanded, + attribute.Syntax); case BoundKind.Call: { var boundCall = (BoundCall)containingExpression; diff --git a/src/Compilers/CSharp/Portable/Parser/Blender.Cursor.cs b/src/Compilers/CSharp/Portable/Parser/Blender.Cursor.cs index e9c1dd0a84f87..4f9b6796441b9 100644 --- a/src/Compilers/CSharp/Portable/Parser/Blender.Cursor.cs +++ b/src/Compilers/CSharp/Portable/Parser/Blender.Cursor.cs @@ -104,7 +104,7 @@ private static int IndexOfNodeInParent(SyntaxNode node) } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public Cursor MoveToFirstChild() diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index d71dba8c8f12b..d364f29da881e 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6780,7 +6780,7 @@ private NameSyntax ParseQualifiedNameRight( } default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -9261,7 +9261,7 @@ private CommonForEachStatementSyntax ParseForEachStatement( var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var variable = ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureTuples, permitTupleDesignation: true, permitScoped: true); + var variable = ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureTuples, permitTupleDesignation: true); var @in = this.EatToken(SyntaxKind.InKeyword, ErrorCode.ERR_InExpected); if (!IsValidForeachVariable(variable)) { @@ -9338,17 +9338,17 @@ private CommonForEachStatementSyntax ParseForEachStatement( // // See also ScanTypeArgumentList where these disambiguation rules are encoded. // - private ExpressionSyntax ParseExpressionOrDeclaration(ParseTypeMode mode, MessageID feature, bool permitTupleDesignation, bool permitScoped) + private ExpressionSyntax ParseExpressionOrDeclaration(ParseTypeMode mode, MessageID feature, bool permitTupleDesignation) { bool isScoped; - return IsPossibleDeclarationExpression(mode, permitTupleDesignation, permitScoped, out isScoped) + return IsPossibleDeclarationExpression(mode, permitTupleDesignation, out isScoped) ? this.ParseDeclarationExpression(mode, feature, isScoped) : this.ParseSubExpression(Precedence.Expression); } - private bool IsPossibleDeclarationExpression(ParseTypeMode mode, bool permitTupleDesignation, bool permitScoped, out bool isScoped) + private bool IsPossibleDeclarationExpression(ParseTypeMode mode, bool permitTupleDesignation, out bool isScoped) { - Debug.Assert(mode == ParseTypeMode.Normal || !permitScoped); + Debug.Assert(mode is ParseTypeMode.Normal or ParseTypeMode.FirstElementOfPossibleTupleLiteral or ParseTypeMode.AfterTupleComma); isScoped = false; if (this.IsInAsync && this.CurrentToken.ContextualKind == SyntaxKind.AwaitKeyword) @@ -9360,13 +9360,35 @@ private bool IsPossibleDeclarationExpression(ParseTypeMode mode, bool permitTupl var resetPoint = this.GetResetPoint(); try { - if (permitScoped && this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword && mode == ParseTypeMode.Normal) + if (this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword) { this.EatToken(); if (ScanType() != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.IdentifierToken) { - isScoped = true; - return true; + switch (mode) + { + case ParseTypeMode.FirstElementOfPossibleTupleLiteral: + if (this.PeekToken(1).Kind == SyntaxKind.CommaToken) + { + isScoped = true; + return true; + } + break; + + case ParseTypeMode.AfterTupleComma: + if (this.PeekToken(1).Kind is SyntaxKind.CommaToken or SyntaxKind.CloseParenToken) + { + isScoped = true; + return true; + } + break; + + default: + // The other case where we disambiguate between a declaration and expression is before the `in` of a foreach loop. + // There we err on the side of accepting a declaration. + isScoped = true; + return true; + } } this.Reset(ref resetPoint); @@ -11805,7 +11827,7 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) // that the ref/out of the argument must match the parameter when binding the argument list. expression = (refKindKeyword?.Kind == SyntaxKind.OutKeyword) - ? ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureOutVar, permitTupleDesignation: false, permitScoped: false) + ? ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureOutVar, permitTupleDesignation: false) : ParseSubExpression(Precedence.Expression); } @@ -12108,7 +12130,7 @@ private ExpressionSyntax ParseCastOrParenExpressionOrTuple() { this.Reset(ref resetPoint); var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true, permitScoped: false); + var expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true); // ( , must be a tuple if (this.CurrentToken.Kind == SyntaxKind.CommaToken) @@ -12121,7 +12143,7 @@ private ExpressionSyntax ParseCastOrParenExpressionOrTuple() if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) { var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); - expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true, permitScoped: false); + expression = this.ParseExpressionOrDeclaration(ParseTypeMode.FirstElementOfPossibleTupleLiteral, feature: 0, permitTupleDesignation: true); var firstArg = _syntaxFactory.Argument(nameColon, refKindKeyword: null, expression: expression); return ParseTupleExpressionTail(openParen, firstArg); @@ -12151,11 +12173,11 @@ private TupleExpressionSyntax ParseTupleExpressionTail(SyntaxToken openParen, Ar ArgumentSyntax arg; - var expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true, permitScoped: false); + var expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) { var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); - expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true, permitScoped: false); + expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); arg = _syntaxFactory.Argument(nameColon, refKindKeyword: null, expression: expression); } else diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 2b26f05ca857c..bed33f4c0216f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -812,20 +812,36 @@ public override void VisitParameter(IParameterSymbol symbol) if (includeType) { - AddParameterRefKindIfNeeded(symbol); - AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); - - if (symbol.ScopedKind == ScopedKind.ScopedValue && - format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) + if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); + // Add 'scoped' unless the parameter is an out parameter or + // 'this' since those cases are implicitly scoped. + if (symbol.ScopedKind == ScopedKind.ScopedRef && + symbol.RefKind != RefKind.Out && + !symbol.IsThis) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } + + AddParameterRefKind(symbol.RefKind); } - if (symbol.IsParams && format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeParamsRefOut)) + AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); + + if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) { - AddKeyword(SyntaxKind.ParamsKeyword); - AddSpace(); + if (symbol.ScopedKind == ScopedKind.ScopedValue && symbol.RefKind == RefKind.None) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } + + if (symbol.IsParams) + { + AddKeyword(SyntaxKind.ParamsKeyword); + AddSpace(); + } } symbol.Type.Accept(this.NotFirstVisitor); @@ -1111,26 +1127,6 @@ private void AddReadOnlyIfNeeded() } } - private void AddParameterRefKindIfNeeded(IParameterSymbol symbol) - { - if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeParamsRefOut)) - { - if (symbol.ScopedKind == ScopedKind.ScopedRef && - !symbol.IsThis && - format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) - { - var parameter = (symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol(); - if (parameter is null || !ParameterHelpers.IsRefScopedByDefault(parameter)) - { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); - } - } - - AddParameterRefKind(symbol.RefKind); - } - } - private void AddParameterRefKind(RefKind refKind) { switch (refKind) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index 4895b9e96e2e2..823daf6d2f694 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -187,33 +187,32 @@ private void AddGlobalNamespace(INamespaceSymbol globalNamespace) public override void VisitLocal(ILocalSymbol symbol) { - if (symbol.IsRef && - format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeRef)) + if (format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeModifiers)) { - if (symbol.ScopedKind == ScopedKind.ScopedRef && - format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) + if (symbol.IsRef) { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); - } + if (symbol.ScopedKind == ScopedKind.ScopedRef) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } - AddKeyword(SyntaxKind.RefKeyword); - AddSpace(); + AddKeyword(SyntaxKind.RefKeyword); + AddSpace(); - if (symbol.RefKind == RefKind.RefReadOnly) + if (symbol.RefKind == RefKind.RefReadOnly) + { + AddKeyword(SyntaxKind.ReadOnlyKeyword); + AddSpace(); + } + } + else if (symbol.ScopedKind == ScopedKind.ScopedValue) { - AddKeyword(SyntaxKind.ReadOnlyKeyword); + AddKeyword(SyntaxKind.ScopedKeyword); AddSpace(); } } - if (symbol.ScopedKind == ScopedKind.ScopedValue && - format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) - { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); - } - if (format.LocalOptions.IncludesOption(SymbolDisplayLocalOptions.IncludeType)) { symbol.Type.Accept(this.NotFirstVisitor); diff --git a/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs b/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs index 69047c25560b4..555fcbf36d926 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs @@ -206,7 +206,7 @@ private ArrayTypeSymbol SubstituteArrayType(ArrayTypeSymbol t) } else if (interfaces.Length != 0) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return ArrayTypeSymbol.CreateSZArray( diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs index c076d393722fe..881ef73069822 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs @@ -733,7 +733,7 @@ internal static MethodSymbol TranslateAnonymousTypeMethodSymbol(MethodSymbol met return ((MethodSymbol)member).AsMember(translatedType); } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs index 8a7435b60529a..06788db9915e5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs @@ -32,11 +32,11 @@ internal AnonymousTypeOrDelegatePublicSymbol(AnonymousTypeManager manager, Anony internal abstract AnonymousTypeOrDelegatePublicSymbol SubstituteTypes(AbstractTypeMap typeMap); protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal sealed override IEnumerable GetFieldsToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override bool HasCodeAnalysisEmbeddedAttribute => false; @@ -154,7 +154,7 @@ internal sealed override ImmutableArray InterfacesNoUseSiteDiag internal sealed override ImmutableArray GetInterfacesToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal abstract override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics { get; } @@ -228,7 +228,7 @@ public sealed override bool IsSerializable public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal sealed override bool HasDeclarativeSecurity @@ -238,7 +238,7 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() @@ -261,7 +261,7 @@ internal sealed override ImmutableArray GetDeclaredInterfaces(C return ImmutableArray.Empty; } - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol? NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs index b9fcf514dd8f9..1e4730727af7a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs @@ -204,7 +204,7 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() @@ -229,10 +229,10 @@ protected SyntheticBoundNodeFactory CreateBoundNodeFactory(TypeCompilationState internal sealed override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs index 28d895ed5300f..ec05349c85c53 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs @@ -51,7 +51,7 @@ internal AnonymousTypeOrDelegateTemplateSymbol(AnonymousTypeManager manager, Loc internal abstract string TypeDescriptorKey { get; } protected sealed override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); /// /// Smallest location of the template, actually contains the smallest location @@ -291,7 +291,7 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() @@ -304,7 +304,7 @@ internal sealed override AttributeUsageInfo GetAttributeUsageInfo() return AttributeUsageInfo.Null; } - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol? NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index 2a8e414837db0..20bc5bc0676c2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -401,7 +401,7 @@ internal ErrorTypeSymbol CreateMultipleForwardingErrorTypeSymbol(ref MetadataTyp /// internal virtual void RegisterDeclaredSpecialType(NamedTypeSymbol corType) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// @@ -412,7 +412,7 @@ internal virtual bool KeepLookingForDeclaredSpecialTypes { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -421,7 +421,7 @@ internal virtual bool KeepLookingForDeclaredSpecialTypes /// internal virtual NamedTypeSymbol GetNativeIntegerType(NamedTypeSymbol underlyingType) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// @@ -442,19 +442,16 @@ internal bool RuntimeSupportsStaticAbstractMembersInInterfaces /// /// Whether the target runtime supports numeric IntPtr types. - /// This test hook should be removed once TargetFramework.Net70 is added. - /// Tracked by https://github.com/dotnet/roslyn/issues/61235 /// - internal virtual bool RuntimeSupportsNumericIntPtr + internal bool RuntimeSupportsNumericIntPtr { get { // CorLibrary should never be null, but that invariant is broken in some cases for MissingAssemblySymbol. // Tracked by https://github.com/dotnet/roslyn/issues/61262 - return CorLibrary?.RuntimeSupportsNumericIntPtr == true; + return CorLibrary is not null && + RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__NumericIntPtr); } - - set => CorLibrary.RuntimeSupportsNumericIntPtr = value; } protected bool RuntimeSupportsFeature(SpecialMember feature) @@ -467,22 +464,8 @@ protected bool RuntimeSupportsFeature(SpecialMember feature) internal bool RuntimeSupportsUnmanagedSignatureCallingConvention => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__UnmanagedSignatureCallingConvention); - internal virtual bool RuntimeSupportsByRefFields - { - get - { - // CorLibrary should never be null, but that invariant is broken in some cases for MissingAssemblySymbol. - // Tracked by https://github.com/dotnet/roslyn/issues/61262 - return CorLibrary?.RuntimeSupportsByRefFields == true; - } - - set - { - // The setter should be removed once TargetFramework.Net70 is added - // Tracked by https://github.com/dotnet/roslyn/issues/61463 - CorLibrary.RuntimeSupportsByRefFields = value; - } - } + internal bool RuntimeSupportsByRefFields + => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefFields); /// /// True if the target runtime support covariant returns of methods declared in classes. diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs index 75e6aa77fbe61..bca8464988947 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs @@ -690,7 +690,7 @@ internal bool ShouldEmitAttribute(Symbol target, bool isReturnType, bool emittin if (HasErrors) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Attribute type is conditionally omitted if both the following are true: diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstructedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ConstructedNamedTypeSymbol.cs index e3e2ee7990f20..0e84a0d3d25f6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstructedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstructedNamedTypeSymbol.cs @@ -42,12 +42,12 @@ public override NamedTypeSymbol ConstructedFrom public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -132,7 +132,7 @@ internal sealed override bool GetUnificationUseSiteDiagnosticRecursive(ref Diagn public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index b10ebb10532fc..595fbd4211769 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -241,7 +241,7 @@ public sealed override DllImportData GetDllImportData() public override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal sealed override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation @@ -261,12 +261,12 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool GenerateDebugInfo diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index fcd3066e63b31..230869b1083c1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -161,7 +161,7 @@ public override ImmutableArray GetMembers(string name) internal sealed override IEnumerable GetFieldsToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetEarlyAttributeDecodingMembers() @@ -509,7 +509,7 @@ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() @@ -529,10 +529,10 @@ internal virtual bool Unreported public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } - internal override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal override NamedTypeSymbol? NativeIntegerUnderlyingType => null; @@ -709,6 +709,6 @@ internal override TypeMap TypeSubstitution } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs index 10a27ed1b48a7..1beb24ad71c41 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs @@ -93,7 +93,7 @@ private static ImmutableArray UnwrapErrorCandidates(ImmutableArray false; internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null; - internal override bool GenerateDebugInfo => throw ExceptionUtilities.Unreachable; - internal override ObsoleteAttributeData? ObsoleteAttributeData => throw ExceptionUtilities.Unreachable; - - public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable; - public override DllImportData GetDllImportData() => throw ExceptionUtilities.Unreachable; - internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => throw ExceptionUtilities.Unreachable; - internal override IEnumerable GetSecurityInformation() => throw ExceptionUtilities.Unreachable; - internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal override bool GenerateDebugInfo => throw ExceptionUtilities.Unreachable(); + internal override ObsoleteAttributeData? ObsoleteAttributeData => throw ExceptionUtilities.Unreachable(); + + public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable(); + public override DllImportData GetDllImportData() => throw ExceptionUtilities.Unreachable(); + internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => throw ExceptionUtilities.Unreachable(); + internal override IEnumerable GetSecurityInformation() => throw ExceptionUtilities.Unreachable(); + internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs index 06411db4b99c8..94725807c68ec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs @@ -109,51 +109,51 @@ internal void SetExpression(BoundExpression expression) public override Symbol? ContainingSymbol => null; - public override ImmutableArray Locations => throw ExceptionUtilities.Unreachable; + public override ImmutableArray Locations => throw ExceptionUtilities.Unreachable(); - public override ImmutableArray DeclaringSyntaxReferences => throw ExceptionUtilities.Unreachable; + public override ImmutableArray DeclaringSyntaxReferences => throw ExceptionUtilities.Unreachable(); - public override Accessibility DeclaredAccessibility => throw ExceptionUtilities.Unreachable; + public override Accessibility DeclaredAccessibility => throw ExceptionUtilities.Unreachable(); public override bool IsStatic => false; - public override bool IsAbstract => throw ExceptionUtilities.Unreachable; + public override bool IsAbstract => throw ExceptionUtilities.Unreachable(); - public override bool IsSealed => throw ExceptionUtilities.Unreachable; + public override bool IsSealed => throw ExceptionUtilities.Unreachable(); internal override NamedTypeSymbol? BaseTypeNoUseSiteDiagnostics => null; - internal override bool IsRecord => throw ExceptionUtilities.Unreachable; + internal override bool IsRecord => throw ExceptionUtilities.Unreachable(); - internal override bool IsRecordStruct => throw ExceptionUtilities.Unreachable; + internal override bool IsRecordStruct => throw ExceptionUtilities.Unreachable(); - internal override ObsoleteAttributeData ObsoleteAttributeData => throw ExceptionUtilities.Unreachable; + internal override ObsoleteAttributeData ObsoleteAttributeData => throw ExceptionUtilities.Unreachable(); - public override void Accept(CSharpSymbolVisitor visitor) => throw ExceptionUtilities.Unreachable; + public override void Accept(CSharpSymbolVisitor visitor) => throw ExceptionUtilities.Unreachable(); - public override TResult Accept(CSharpSymbolVisitor visitor) => throw ExceptionUtilities.Unreachable; + public override TResult Accept(CSharpSymbolVisitor visitor) => throw ExceptionUtilities.Unreachable(); - public override ImmutableArray GetMembers() => throw ExceptionUtilities.Unreachable; + public override ImmutableArray GetMembers() => throw ExceptionUtilities.Unreachable(); - public override ImmutableArray GetMembers(string name) => throw ExceptionUtilities.Unreachable; + public override ImmutableArray GetMembers(string name) => throw ExceptionUtilities.Unreachable(); - public override ImmutableArray GetTypeMembers() => throw ExceptionUtilities.Unreachable; + public override ImmutableArray GetTypeMembers() => throw ExceptionUtilities.Unreachable(); - public override ImmutableArray GetTypeMembers(string name) => throw ExceptionUtilities.Unreachable; + public override ImmutableArray GetTypeMembers(string name) => throw ExceptionUtilities.Unreachable(); - protected override ISymbol CreateISymbol() => throw ExceptionUtilities.Unreachable; + protected override ISymbol CreateISymbol() => throw ExceptionUtilities.Unreachable(); - protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) => throw ExceptionUtilities.Unreachable; + protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) => throw ExceptionUtilities.Unreachable(); - internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument a) => throw ExceptionUtilities.Unreachable; + internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument a) => throw ExceptionUtilities.Unreachable(); - internal override void AddNullableTransforms(ArrayBuilder transforms) => throw ExceptionUtilities.Unreachable; + internal override void AddNullableTransforms(ArrayBuilder transforms) => throw ExceptionUtilities.Unreachable(); - internal override bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeSymbol result) => throw ExceptionUtilities.Unreachable; + internal override bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeSymbol result) => throw ExceptionUtilities.Unreachable(); - internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => throw ExceptionUtilities.Unreachable; + internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => throw ExceptionUtilities.Unreachable(); - internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) => throw ExceptionUtilities.Unreachable; + internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) => throw ExceptionUtilities.Unreachable(); internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList? basesBeingResolved = null) => ImmutableArray.Empty; @@ -194,7 +194,7 @@ private FunctionTypeSymbol WithDelegateType(NamedTypeSymbol delegateType) new FunctionTypeSymbol(delegateType); } - internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() => throw ExceptionUtilities.Unreachable; + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() => throw ExceptionUtilities.Unreachable(); internal override bool Equals(TypeSymbol t2, TypeCompareKind compareKind) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs index ac90c8151ecfc..c2af009c64bd6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs @@ -142,7 +142,7 @@ protected override TypeSymbol LookupTopLevelTypeDefSymbol( } catch (Exception e) when (FatalError.ReportAndPropagate(e)) // Trying to get more useful Watson dumps. { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index f2a599741f916..7c9eceb72e0ee 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -456,7 +456,7 @@ public override DllImportData GetDllImportData() => HasFlag(MethodAttributes.Pin internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Accessibility DeclaredAccessibility @@ -1050,7 +1050,7 @@ public override ImmutableArray GetAttributes() internal override byte? GetLocalNullableContextValue() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override MethodKind MethodKind @@ -1514,7 +1514,7 @@ protected override bool HasSetsRequiredMembersImpl internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ObsoleteAttributeData ObsoleteAttributeData @@ -1603,17 +1603,17 @@ internal override OverriddenOrHiddenMembersResult OverriddenOrHiddenMembers internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } // perf, not correctness @@ -1625,7 +1625,7 @@ public override bool AreLocalsZeroed // Internal for unit test internal bool TestIsExtensionBitTrue => _packedFlags.IsExtensionMethod; - internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs index a43ed0dce3c13..09dc7ea31e98c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs @@ -144,7 +144,7 @@ public sealed override bool AreLocalsZeroed { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -659,7 +659,7 @@ internal override CharSet? DefaultMarshallingCharSet get { // not used by the compiler - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -811,48 +811,7 @@ internal override bool UseUpdatedEscapeRules private bool CalculateUseUpdatedRules() { // [RefSafetyRules(version)], regardless of version. - if (_module.HasRefSafetyRulesAttribute(Token, out _)) - { - return true; - } - - // System.Runtime { ver: 7.0 } - if (IsOrReferencesSystemRuntimeCorLibrary(_module, out var corlibVersion) && corlibVersion is { Major: 7, Minor: 0 }) - { - return true; - } - - return false; - } - - private static bool IsOrReferencesSystemRuntimeCorLibrary(PEModule module, out Version? version) - { - const string corlibName = "System.Runtime"; - - AssemblyIdentity? identity; - try - { - identity = module.ReadAssemblyIdentityOrThrow(); - } - catch (BadImageFormatException) - { - identity = null; - } - if (identity?.Name == corlibName) - { - version = identity.Version; - return true; - } - - var assemblyHandle = module.GetAssemblyRef(corlibName); - if (!assemblyHandle.IsNil) - { - version = module.GetAssemblyRef(assemblyHandle).Version; - return true; - } - - version = null; - return false; + return _module.HasRefSafetyRulesAttribute(Token, out _); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 6fed704fcad57..75749ffe450d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -320,7 +320,7 @@ private PENamedTypeSymbol( if (_lazyUncommonProperties is not null) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // when a file-local type from source is loaded from metadata, we do a best-effort check to identify it as a file type @@ -743,7 +743,7 @@ internal override IEnumerable GetCustomAttributesToEmit(PEM internal override byte? GetLocalNullableContextValue() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IEnumerable MemberNames @@ -1136,7 +1136,7 @@ internal override IEnumerable GetMethodsToEmit() } // Ensure we explicitly returned from inside loop. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1693,7 +1693,7 @@ internal TypeAttributes Flags public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override bool MightContainExtensionMethods @@ -2287,7 +2287,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override NamedTypeSymbol ComImportCoClass @@ -2452,7 +2452,7 @@ internal PENamedTypeSymbolNonGeneric( } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override int Arity { @@ -2532,7 +2532,7 @@ internal PENamedTypeSymbolGeneric( } protected sealed override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override int Arity { @@ -2576,7 +2576,7 @@ public override ImmutableArray TypeParameters } } - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs index 334bbfffea522..f806c65ab6866 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs @@ -32,70 +32,6 @@ internal abstract class MetadataOrSourceAssemblySymbol private int _cachedSpecialTypes; private NativeIntegerTypeSymbol[] _lazyNativeIntegerTypes; - private ThreeState _lazyRuntimeSupportsNumericIntPtr = ThreeState.Unknown; - private ThreeState _lazyRuntimeSupportsByRefFields = ThreeState.Unknown; - - internal override bool RuntimeSupportsNumericIntPtr - { - get - { - if ((object)CorLibrary == this) - { - if (!_lazyRuntimeSupportsNumericIntPtr.HasValue()) - { - _lazyRuntimeSupportsNumericIntPtr = RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__NumericIntPtr).ToThreeState(); - } - - return _lazyRuntimeSupportsNumericIntPtr.Value(); - } - - return base.RuntimeSupportsNumericIntPtr; - } - set - { - Debug.Assert(value); - Debug.Assert(!RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__NumericIntPtr)); - if ((object)CorLibrary == this) - { - Debug.Assert(!_lazyRuntimeSupportsNumericIntPtr.HasValue()); - _lazyRuntimeSupportsNumericIntPtr = value.ToThreeState(); - return; - } - - base.RuntimeSupportsNumericIntPtr = value; - } - } - - internal override bool RuntimeSupportsByRefFields - { - get - { - if ((object)CorLibrary == this) - { - if (!_lazyRuntimeSupportsByRefFields.HasValue()) - { - _lazyRuntimeSupportsByRefFields = RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefFields).ToThreeState(); - } - - return _lazyRuntimeSupportsByRefFields.Value(); - } - - return base.RuntimeSupportsByRefFields; - } - set - { - Debug.Assert(value); - Debug.Assert(!RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefFields)); - if ((object)CorLibrary == this) - { - Debug.Assert(!_lazyRuntimeSupportsByRefFields.HasValue()); - _lazyRuntimeSupportsByRefFields = value.ToThreeState(); - return; - } - - base.RuntimeSupportsByRefFields = value; - } - } /// /// Lookup declaration for predefined CorLib type in this Assembly. diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 0dbc34c017ffa..08e070a3b00e3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -279,7 +279,7 @@ internal ParameterSymbol ThisParameter ParameterSymbol thisParameter; if (!TryGetThisParameter(out thisParameter)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return thisParameter; } @@ -1090,7 +1090,7 @@ internal virtual bool IsIterator internal virtual TypeWithAnnotations IteratorElementTypeWithAnnotations { get { return default; } - set { throw ExceptionUtilities.Unreachable; } + set { throw ExceptionUtilities.Unreachable(); } } /// @@ -1100,7 +1100,7 @@ internal virtual TypeWithAnnotations IteratorElementTypeWithAnnotations /// internal virtual void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index 413a9e308c56c..a0d97523b981c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -225,5 +225,17 @@ internal static bool IsValidUnscopedRefAttributeTarget(this MethodSymbol method) !method.IsConstructor() && !method.IsInitOnly; } + +#nullable enable + internal static ImmutableArray GetParameterEffectiveScopes(this MethodSymbol? method) + { + if (method is null) + return default; + + if (method.Parameters.All(p => p.EffectiveScope == DeclarationScope.Unscoped)) + return default; + + return method.Parameters.SelectAsArray(p => p.EffectiveScope); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs index 79dd230fc637a..3c0a8ead3291b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs @@ -119,7 +119,7 @@ public override ImmutableArray Locations internal override void SetLinkedReferencedAssemblies(ImmutableArray assemblies) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetLinkedReferencedAssemblies() @@ -129,7 +129,7 @@ internal override ImmutableArray GetLinkedReferencedAssemblies() internal override void SetNoPiaResolutionAssemblies(ImmutableArray assemblies) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetNoPiaResolutionAssemblies() @@ -170,7 +170,7 @@ internal override NamedTypeSymbol LookupTopLevelMetadataTypeWithCycleDetection(r internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool AreInternalsVisibleToThisAssembly(AssemblySymbol other) diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs index 2407fa54f5d52..d0216601874b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs @@ -64,35 +64,5 @@ internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) return _lazySpecialTypes[(int)type]; } - - internal override bool RuntimeSupportsNumericIntPtr - { - get - { - // For now we assume that it is not supported by default - Debug.Assert((object)CorLibrary == this); - return false; - } - set - { - Debug.Assert((object)CorLibrary == this); - throw ExceptionUtilities.Unreachable; - } - } - - internal override bool RuntimeSupportsByRefFields - { - get - { - // For now we assume that it is not supported by default - Debug.Assert((object)CorLibrary == this); - return false; - } - set - { - Debug.Assert((object)CorLibrary == this); - throw ExceptionUtilities.Unreachable; - } - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs index d2407b38733d6..3d62eda77d53f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs @@ -444,7 +444,7 @@ public override SpecialType SpecialType protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int GetHashCode() diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs index 025bd36ba58d4..6652597825f94 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs @@ -160,7 +160,7 @@ internal override ImmutableArray GetReferencedAssemblySymbols() internal override void SetReferences(ModuleReferences moduleReferences, SourceAssemblySymbol originatingSourceAssemblyDebugOnly) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool HasUnifiedReferences @@ -170,7 +170,7 @@ internal override bool HasUnifiedReferences internal override bool GetUnificationUseSiteDiagnostic(ref DiagnosticInfo result, TypeSymbol dependentType) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool HasAssemblyCompilationRelaxationsAttribute @@ -192,7 +192,7 @@ internal override CharSet? DefaultMarshallingCharSet public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal sealed override bool UseUpdatedEscapeRules => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs index 7000ac0a23cf9..11ffcbe61a700 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs @@ -334,7 +334,7 @@ internal AssemblySymbol GetReferencedAssemblySymbol(int referencedAssemblyIndex) internal virtual ImmutableArray GetHash(AssemblyHashAlgorithm algorithmId) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index cd7ad97c894f0..0be029a801186 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -149,17 +149,17 @@ ImmutableArray makeMembers(ImmutableArray underlyingMembers) internal override ImmutableArray GetDeclaredInterfaces(ConsList basesBeingResolved) => GetInterfaces(basesBeingResolved); - internal override ImmutableArray GetEarlyAttributeDecodingMembers() => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray GetEarlyAttributeDecodingMembers() => throw ExceptionUtilities.Unreachable(); - internal override ImmutableArray GetEarlyAttributeDecodingMembers(string name) => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray GetEarlyAttributeDecodingMembers(string name) => throw ExceptionUtilities.Unreachable(); - internal override IEnumerable GetFieldsToEmit() => throw ExceptionUtilities.Unreachable; + internal override IEnumerable GetFieldsToEmit() => throw ExceptionUtilities.Unreachable(); - internal override ImmutableArray GetInterfacesToEmit() => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray GetInterfacesToEmit() => throw ExceptionUtilities.Unreachable(); internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList? basesBeingResolved = null) => GetInterfaces(basesBeingResolved); - protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable; + protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable(); internal override UseSiteInfo GetUseSiteInfo() { @@ -168,11 +168,11 @@ internal override UseSiteInfo GetUseSiteInfo() return useSiteInfo; } - public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable; + public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable(); internal override bool IsNativeIntegerWrapperType => true; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => _underlyingType; @@ -209,7 +209,7 @@ internal override bool Equals(TypeSymbol? other, TypeCompareKind comparison) void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endif @@ -354,10 +354,10 @@ public override ImmutableArray Parameters internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); public override bool Equals(Symbol? other, TypeCompareKind comparison) => NativeIntegerTypeSymbol.EqualsHelper(this, other, comparison, symbol => symbol.UnderlyingMethod); @@ -367,7 +367,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endif } @@ -415,7 +415,7 @@ internal NativeIntegerParameterSymbol(NativeIntegerTypeSymbol containingType, Na void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endif } @@ -464,7 +464,7 @@ internal NativeIntegerPropertySymbol( void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { // Emit should use underlying symbol only. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #endif } diff --git a/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs index 1c06e29b20673..aaf909426c4f5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NullableAnnotationExtensions.cs @@ -9,9 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; -// https://github.com/dotnet/roslyn/issues/34962 IDE005 "Fix formatting" does a poor job with a switch expression as the body of an expression-bodied method -#pragma warning disable IDE0055 - namespace Microsoft.CodeAnalysis.CSharp { internal static class NullableAnnotationExtensions diff --git a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersResult.cs b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersResult.cs index bb9ed641b2cd0..29e1e10a9ba63 100644 --- a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersResult.cs +++ b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersResult.cs @@ -74,7 +74,7 @@ internal static Symbol GetOverriddenMember(Symbol substitutedOverridingMember, S } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return null; diff --git a/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs index a58211dfcd130..8545ec720d59c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PlaceholderTypeArgumentSymbol.cs @@ -28,7 +28,7 @@ private PlaceholderTypeArgumentSymbol() protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override string Name diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index d046a7d009b5a..c418a92b427e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -323,7 +323,7 @@ public override DllImportData GetDllImportData() public override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation @@ -570,10 +570,10 @@ private ImmutableArray MakeParameters() internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); public override bool Equals(Symbol obj, TypeCompareKind compareKind) { @@ -588,7 +588,7 @@ public override int GetHashCode() return _reducedFrom.GetHashCode(); } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; @@ -634,30 +634,30 @@ public override ImmutableArray RefCustomModifiers internal override bool IsCallerLineNumber { // ReducedExtensionMethodParameterSymbol is only exposed to semantic model. - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsCallerFilePath { // ReducedExtensionMethodParameterSymbol is only exposed to semantic model. - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsCallerMemberName { // ReducedExtensionMethodParameterSymbol is only exposed to semantic model. - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override int CallerArgumentExpressionParameterIndex { // ReducedExtensionMethodParameterSymbol is only exposed to semantic model. - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } - internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable(); - internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable(); public sealed override bool Equals(Symbol obj, TypeCompareKind compareKind) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs index da4dc6aaf714d..242448bcfa052 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs @@ -213,7 +213,7 @@ internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) { // Cor library should not have any references and, therefore, should never be // wrapped by a RetargetingAssemblySymbol. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetNoPiaResolutionAssemblies() diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs index 39f7ebdd5567c..781406109136f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs @@ -372,9 +372,9 @@ internal override bool GenerateDebugInfo internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { // retargeting symbols refer to a symbol from another compilation, they don't define locals in the current compilation - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs index dc34c1b00e8a7..d5738c21f7d15 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs @@ -309,7 +309,7 @@ public sealed override bool AreLocalsZeroed { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index 186d67cc88a9a..628fe81e55313 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -372,12 +372,12 @@ internal override bool IsComImport public sealed override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override FileIdentifier? AssociatedFileIdentifier => _underlyingType.AssociatedFileIdentifier; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs index abbf2c63870c8..c976091464ad3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs @@ -1085,7 +1085,7 @@ public override TypeParameterSymbol Retarget(TypeParameterSymbol typeParameter) } while (containingType is object); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1385,7 +1385,7 @@ public override Symbol VisitMethod(MethodSymbol symbol, RetargetOptions options) public override Symbol VisitParameter(ParameterSymbol symbol, RetargetOptions options) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Symbol VisitField(FieldSymbol symbol, RetargetOptions options) diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index e5153dea9674d..279464bc50d4f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -94,75 +94,75 @@ public SignatureOnlyMethodSymbol( public override string Name { get { return _name; } } - internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); #region Not used by MethodSignatureComparer - internal override bool GenerateDebugInfo { get { throw ExceptionUtilities.Unreachable; } } + internal override bool GenerateDebugInfo { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool HasSpecialName { get { throw ExceptionUtilities.Unreachable; } } + internal override bool HasSpecialName { get { throw ExceptionUtilities.Unreachable(); } } - internal override System.Reflection.MethodImplAttributes ImplementationAttributes { get { throw ExceptionUtilities.Unreachable; } } + internal override System.Reflection.MethodImplAttributes ImplementationAttributes { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool RequiresSecurityObject { get { throw ExceptionUtilities.Unreachable; } } + internal override bool RequiresSecurityObject { get { throw ExceptionUtilities.Unreachable(); } } public override DllImportData GetDllImportData() { return null; } - internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { get { throw ExceptionUtilities.Unreachable; } } + internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool HasDeclarativeSecurity { get { throw ExceptionUtilities.Unreachable; } } + internal override bool HasDeclarativeSecurity { get { throw ExceptionUtilities.Unreachable(); } } - internal override IEnumerable GetSecurityInformation() { throw ExceptionUtilities.Unreachable; } + internal override IEnumerable GetSecurityInformation() { throw ExceptionUtilities.Unreachable(); } - internal override ObsoleteAttributeData ObsoleteAttributeData { get { throw ExceptionUtilities.Unreachable; } } + internal override ObsoleteAttributeData ObsoleteAttributeData { get { throw ExceptionUtilities.Unreachable(); } } - internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable; + internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable(); - internal override ImmutableArray GetAppliedConditionalSymbols() { throw ExceptionUtilities.Unreachable; } + internal override ImmutableArray GetAppliedConditionalSymbols() { throw ExceptionUtilities.Unreachable(); } - public override ImmutableArray TypeArgumentsWithAnnotations { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray TypeArgumentsWithAnnotations { get { throw ExceptionUtilities.Unreachable(); } } - public override Symbol AssociatedSymbol { get { throw ExceptionUtilities.Unreachable; } } + public override Symbol AssociatedSymbol { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsExtensionMethod { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsExtensionMethod { get { throw ExceptionUtilities.Unreachable(); } } - public override bool HidesBaseMethodsByName { get { throw ExceptionUtilities.Unreachable; } } + public override bool HidesBaseMethodsByName { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable(); } } - public override Accessibility DeclaredAccessibility { get { throw ExceptionUtilities.Unreachable; } } + public override Accessibility DeclaredAccessibility { get { throw ExceptionUtilities.Unreachable(); } } public override bool IsStatic { get { return _isStatic; } } - public override bool IsAsync { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsAsync { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsVirtual { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsVirtual { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsOverride { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsOverride { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsAbstract { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsAbstract { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsSealed { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsSealed { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsExtern { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsExtern { get { throw ExceptionUtilities.Unreachable(); } } - public override bool AreLocalsZeroed { get { throw ExceptionUtilities.Unreachable; } } + public override bool AreLocalsZeroed { get { throw ExceptionUtilities.Unreachable(); } } - public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable; } } + public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable(); } } - internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable; } } + internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable(); } } - internal sealed override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) { throw ExceptionUtilities.Unreachable; } + internal sealed override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) { throw ExceptionUtilities.Unreachable(); } - internal sealed override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) { throw ExceptionUtilities.Unreachable; } + internal sealed override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) { throw ExceptionUtilities.Unreachable(); } internal override bool IsMetadataFinal { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -170,9 +170,9 @@ internal override bool IsMetadataFinal internal override bool IsInitOnly => _isInitOnly; - internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { throw ExceptionUtilities.Unreachable; } + internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { throw ExceptionUtilities.Unreachable(); } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs index e9b60c4f57ccd..95c13a23b9a14 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs @@ -55,47 +55,47 @@ internal override DeclarationScope EffectiveScope #region Not used by MethodSignatureComparer - internal override bool IsMetadataIn { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsMetadataIn { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsMetadataOut { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsMetadataOut { get { throw ExceptionUtilities.Unreachable(); } } - internal override MarshalPseudoCustomAttributeData MarshallingInformation { get { throw ExceptionUtilities.Unreachable; } } + internal override MarshalPseudoCustomAttributeData MarshallingInformation { get { throw ExceptionUtilities.Unreachable(); } } - public override int Ordinal { get { throw ExceptionUtilities.Unreachable; } } + public override int Ordinal { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsMetadataOptional { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsMetadataOptional { get { throw ExceptionUtilities.Unreachable(); } } - internal override ConstantValue ExplicitDefaultConstantValue { get { throw ExceptionUtilities.Unreachable; } } + internal override ConstantValue ExplicitDefaultConstantValue { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsIDispatchConstant { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsIDispatchConstant { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsIUnknownConstant { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsIUnknownConstant { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsCallerFilePath { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsCallerFilePath { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsCallerLineNumber { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsCallerLineNumber { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsCallerMemberName { get { throw ExceptionUtilities.Unreachable; } } + internal override bool IsCallerMemberName { get { throw ExceptionUtilities.Unreachable(); } } - internal override int CallerArgumentExpressionParameterIndex { get { throw ExceptionUtilities.Unreachable; } } + internal override int CallerArgumentExpressionParameterIndex { get { throw ExceptionUtilities.Unreachable(); } } - internal override FlowAnalysisAnnotations FlowAnalysisAnnotations { get { throw ExceptionUtilities.Unreachable; } } + internal override FlowAnalysisAnnotations FlowAnalysisAnnotations { get { throw ExceptionUtilities.Unreachable(); } } - internal override ImmutableHashSet NotNullIfParameterNotNull { get { throw ExceptionUtilities.Unreachable; } } + internal override ImmutableHashSet NotNullIfParameterNotNull { get { throw ExceptionUtilities.Unreachable(); } } - public override Symbol ContainingSymbol { get { throw ExceptionUtilities.Unreachable; } } + public override Symbol ContainingSymbol { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable(); } } - public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable; } } + public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable(); } } - internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable; } } + internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable(); } } - internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable(); - internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable(); #endregion Not used by MethodSignatureComparer diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs index 58fc55748f0c9..12f55640560b6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs @@ -67,41 +67,41 @@ public SignatureOnlyPropertySymbol( #region Not used by PropertySignatureComparer - internal override bool HasSpecialName { get { throw ExceptionUtilities.Unreachable; } } + internal override bool HasSpecialName { get { throw ExceptionUtilities.Unreachable(); } } - internal override Cci.CallingConvention CallingConvention { get { throw ExceptionUtilities.Unreachable; } } + internal override Cci.CallingConvention CallingConvention { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable(); } } - public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable; } } + public override ImmutableArray DeclaringSyntaxReferences { get { throw ExceptionUtilities.Unreachable(); } } - public override Accessibility DeclaredAccessibility { get { throw ExceptionUtilities.Unreachable; } } + public override Accessibility DeclaredAccessibility { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsVirtual { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsVirtual { get { throw ExceptionUtilities.Unreachable(); } } public override bool IsOverride => false; - public override bool IsAbstract { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsAbstract { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsSealed { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsSealed { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsExtern { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsExtern { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool IsRequired => throw ExceptionUtilities.Unreachable; + internal override bool IsRequired => throw ExceptionUtilities.Unreachable(); - internal override ObsoleteAttributeData ObsoleteAttributeData { get { throw ExceptionUtilities.Unreachable; } } + internal override ObsoleteAttributeData ObsoleteAttributeData { get { throw ExceptionUtilities.Unreachable(); } } - public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable; } } + public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable(); } } - internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable; } } + internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable(); } } - internal override bool MustCallMethodsDirectly { get { throw ExceptionUtilities.Unreachable; } } + internal override bool MustCallMethodsDirectly { get { throw ExceptionUtilities.Unreachable(); } } - public override MethodSymbol SetMethod { get { throw ExceptionUtilities.Unreachable; } } + public override MethodSymbol SetMethod { get { throw ExceptionUtilities.Unreachable(); } } - public override MethodSymbol GetMethod { get { throw ExceptionUtilities.Unreachable; } } + public override MethodSymbol GetMethod { get { throw ExceptionUtilities.Unreachable(); } } - public override bool IsIndexer { get { throw ExceptionUtilities.Unreachable; } } + public override bool IsIndexer { get { throw ExceptionUtilities.Unreachable(); } } #endregion Not used by PropertySignatureComparer } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs index fc58366c6ee63..e3ee9a0c24d67 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs @@ -202,13 +202,13 @@ internal override ImmutableArray GetInterfaces(ConsList inProgress) { // Constraints are not checked in crefs, so this should never be examined. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override TypeSymbol GetDeducedBaseType(ConsList inProgress) { // Constraints are not checked in crefs, so this should never be examined. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override bool IsImplicitlyDeclared diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs index 3e583e0522e0a..f2845c6f38978 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs @@ -137,7 +137,7 @@ public static T SubstituteExplicitInterfaceImplementation(T unsubstitutedProp } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 47deee5399c82..d78dc829742e3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -50,7 +50,7 @@ internal static GlobalExpressionVariable Create( Debug.Assert(nodeToBind.Kind() == SyntaxKind.VariableDeclarator || nodeToBind is ExpressionSyntax); var syntaxReference = syntax.GetReference(); - return (typeSyntax == null || typeSyntax.IsVar) + return (typeSyntax == null || typeSyntax.SkipScoped(out _).SkipRef(out _).IsVar) ? new InferrableGlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location, containingFieldOpt, nodeToBind) : new GlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location); } @@ -88,7 +88,7 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB if (typeSyntax != null) { - type = binder.BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar); + type = binder.BindTypeOrVarKeyword(typeSyntax.SkipScoped(out _).SkipRef(out _), diagnostics, out isVar); } else { @@ -155,7 +155,7 @@ internal TypeWithAnnotations SetTypeWithAnnotations(TypeWithAnnotations type, Bi protected virtual void InferFieldType(ConsList fieldsBeingBound, Binder binder) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private class InferrableGlobalExpressionVariable : GlobalExpressionVariable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs index 8a64f4b4317f0..ef2321f5811db 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs @@ -29,7 +29,7 @@ internal ImplicitNamedTypeSymbol(NamespaceOrTypeSymbol containingSymbol, MergedT } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override ImmutableArray GetAttributes() { @@ -150,7 +150,7 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetAppliedConditionalSymbols() @@ -167,7 +167,7 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal override bool IsInterpolatedStringHandlerType => false; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaParameterSymbol.cs index 354ec2cde8bcc..ebba2cf1c51d7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaParameterSymbol.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -11,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal sealed class LambdaParameterSymbol : SourceComplexParameterSymbolBase { private readonly SyntaxList _attributeLists; + private readonly DeclarationScope? _effectiveScope; public LambdaParameterSymbol( LambdaSymbol owner, @@ -18,18 +20,23 @@ public LambdaParameterSymbol( TypeWithAnnotations parameterType, int ordinal, RefKind refKind, - DeclarationScope scope, + DeclarationScope? declaredScope, + DeclarationScope? effectiveScope, string name, bool isDiscard, ImmutableArray locations) - : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef: null, isParams: false, isExtensionMethodThis: false, scope) + : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef: null, isParams: false, isExtensionMethodThis: false, scope: declaredScope) { + Debug.Assert(declaredScope.HasValue != effectiveScope.HasValue); _attributeLists = attributeLists; + _effectiveScope = effectiveScope; IsDiscard = isDiscard; } public override bool IsDiscard { get; } + internal override DeclarationScope EffectiveScope => _effectiveScope ?? base.EffectiveScope; + internal override bool IsMetadataOptional { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index de5adcea6a783..ffde187da62f3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -42,12 +43,14 @@ public LambdaSymbol( UnboundLambda unboundLambda, ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes, RefKind refKind, TypeWithAnnotations returnType) : base(unboundLambda.Syntax.GetReference()) { Debug.Assert(syntaxReferenceOpt is not null); Debug.Assert(containingSymbol.DeclaringCompilation == compilation); + Debug.Assert(parameterEffectiveScopes.IsDefault || parameterTypes.Length == parameterEffectiveScopes.Length); _binder = binder; _containingSymbol = containingSymbol; @@ -62,7 +65,7 @@ public LambdaSymbol( _isAsync = unboundLambda.IsAsync; _isStatic = unboundLambda.IsStatic; // No point in making this lazy. We are always going to need these soon after creation of the symbol. - _parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds); + _parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds, parameterEffectiveScopes); _declarationDiagnostics = new BindingDiagnosticBag(); } @@ -304,9 +307,11 @@ private ImmutableArray MakeParameters( CSharpCompilation compilation, UnboundLambda unboundLambda, ImmutableArray parameterTypes, - ImmutableArray parameterRefKinds) + ImmutableArray parameterRefKinds, + ImmutableArray parameterEffectiveScopes) { Debug.Assert(parameterTypes.Length == parameterRefKinds.Length); + Debug.Assert(parameterEffectiveScopes.IsDefault || parameterTypes.Length == parameterEffectiveScopes.Length); if (!unboundLambda.HasSignature || unboundLambda.ParameterCount == 0) { @@ -317,8 +322,9 @@ private ImmutableArray MakeParameters( type, ordinal, arg.refKinds[ordinal], - GeneratedNames.LambdaCopyParameterName(ordinal)), // Make sure nothing binds to this. - (owner: this, refKinds: parameterRefKinds)); + GeneratedNames.LambdaCopyParameterName(ordinal), // Make sure nothing binds to this. + getScope(arg.parameterEffectiveScopes, ordinal)), + (owner: this, refKinds: parameterRefKinds, parameterEffectiveScopes)); } var builder = ArrayBuilder.GetInstance(unboundLambda.ParameterCount); @@ -335,26 +341,28 @@ private ImmutableArray MakeParameters( TypeWithAnnotations type; RefKind refKind; - DeclarationScope scope; + DeclarationScope? declaredScope; + DeclarationScope? effectiveScope; if (hasExplicitlyTypedParameterList) { type = unboundLambda.ParameterTypeWithAnnotations(p); refKind = unboundLambda.RefKind(p); - scope = unboundLambda.DeclaredScope(p); + declaredScope = unboundLambda.DeclaredScope(p); + effectiveScope = null; } else if (p < numDelegateParameters) { type = parameterTypes[p]; refKind = parameterRefKinds[p]; - // https://github.com/dotnet/roslyn/issues/62080: DeclarationScope should be taken from delegate signature. - // We probably should propagate effective scope from the target delegate and make sure parameter symbol doesn't adjust it in any way. - scope = DeclarationScope.Unscoped; + declaredScope = null; + effectiveScope = getScope(parameterEffectiveScopes, p); } else { type = TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(compilation, name: string.Empty, arity: 0, errorInfo: null)); refKind = RefKind.None; - scope = DeclarationScope.Unscoped; + declaredScope = DeclarationScope.Unscoped; + effectiveScope = null; } var attributeLists = unboundLambda.ParameterAttributes(p); @@ -362,13 +370,17 @@ private ImmutableArray MakeParameters( var location = unboundLambda.ParameterLocation(p); var locations = location == null ? ImmutableArray.Empty : ImmutableArray.Create(location); - var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, scope, name, unboundLambda.ParameterIsDiscard(p), locations); + var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, declaredScope, effectiveScope, name, unboundLambda.ParameterIsDiscard(p), locations); builder.Add(parameter); } var result = builder.ToImmutableAndFree(); - return result; + + static DeclarationScope getScope(ImmutableArray scopes, int ordinal) + { + return scopes.IsDefault ? DeclarationScope.Unscoped : scopes[ordinal]; + } } public sealed override bool Equals(Symbol symbol, TypeCompareKind compareKind) @@ -412,10 +424,10 @@ internal override bool GenerateDebugInfo internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); protected override void NoteAttributesComplete(bool forReturnType) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index ee8783d7fec44..18a281f05623b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -385,7 +385,7 @@ protected override void NoteAttributesComplete(bool forReturnType) { } internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter) @@ -512,7 +512,7 @@ public override ImmutableArray GetTypeParameterCons return _lazyTypeParameterConstraintKinds; } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); public override int GetHashCode() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index 39a69daa11ac2..63f6c7ebb81f3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -1844,7 +1844,7 @@ internal override ImmutableArray GetNoPiaResolutionAssemblies() internal override void SetNoPiaResolutionAssemblies(ImmutableArray assemblies) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetLinkedReferencedAssemblies() @@ -1858,7 +1858,7 @@ internal override void SetLinkedReferencedAssemblies(ImmutableArray NotNullIfParameterNotNull get { return ImmutableHashSet.Empty; } } - internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable(); - internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable(); #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 041d6285ddc0d..c80144a088859 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -46,7 +46,7 @@ protected SourceComplexParameterSymbolBase( SyntaxReference syntaxRef, bool isParams, bool isExtensionMethodThis, - DeclarationScope scope) + DeclarationScope? scope) : base(owner, parameterType, ordinal, refKind, scope, name, locations) { Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter))); @@ -198,7 +198,7 @@ internal bool HasEnumeratorCancellationAttribute #nullable enable - internal sealed override DeclarationScope EffectiveScope + internal override DeclarationScope EffectiveScope { get { @@ -572,6 +572,30 @@ internal sealed override CustomAttributesBag GetAttributesB return _lazyCustomAttributesBag; } + /// + /// Binds attributes applied to this parameter. + /// + public ImmutableArray BindParameterAttributes() + { + var binder = new ContextualAttributeBinder(WithTypeParametersBinderOpt, this); + var boundAttributeArrayBuilder = ArrayBuilder.GetInstance(); + foreach (var attributeListSyntaxList in GetAttributeDeclarations()) + { + foreach (var attributeListSyntax in attributeListSyntaxList) + { + foreach (var attributeSyntax in attributeListSyntax.Attributes) + { + var boundType = binder.BindType(attributeSyntax.Name, BindingDiagnosticBag.Discarded); + var boundTypeSymbol = (NamedTypeSymbol)boundType.Type; + var boundAttribute = new ExecutableCodeBinder(attributeSyntax, binder.ContainingMemberOrLambda, binder) + .BindAttribute(attributeSyntax, boundTypeSymbol, this, BindingDiagnosticBag.Discarded); + boundAttributeArrayBuilder.Add(boundAttribute); + } + } + } + return boundAttributeArrayBuilder.ToImmutableAndFree(); + } + internal override void EarlyDecodeWellKnownAttributeType(NamedTypeSymbol attributeType, AttributeSyntax attributeSyntax) { Debug.Assert(!attributeType.IsErrorType()); @@ -1271,7 +1295,7 @@ private void DecodeInterpolatedStringHandlerArgumentAttribute(ref DecodeWellKnow } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } var parameterWellKnownAttributeData = arguments.GetOrCreateData(); @@ -1479,7 +1503,7 @@ internal SourceComplexParameterSymbol( SyntaxReference syntaxRef, bool isParams, bool isExtensionMethodThis, - DeclarationScope scope) + DeclarationScope? scope) : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, isParams, isExtensionMethodThis, scope) { } @@ -1502,7 +1526,7 @@ internal SourceComplexParameterSymbolWithCustomModifiersPrecedingRef( SyntaxReference syntaxRef, bool isParams, bool isExtensionMethodThis, - DeclarationScope scope) + DeclarationScope? scope) : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, isParams, isExtensionMethodThis, scope) { Debug.Assert(!refCustomModifiers.IsEmpty); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index a2746440f0136..7c226133c0bb3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -242,7 +242,7 @@ internal sealed override int CalculateLocalSyntaxOffset(int position, SyntaxTree } // we haven't found the constructor part that declares the variable: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal abstract override bool IsNullableAnalysisEnabled(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs index dfc78d31c7cd5..67943da529e62 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs @@ -237,7 +237,7 @@ internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics => ContainingAssembly.GetSpecialType(SpecialType.System_ValueType); public sealed override bool AreLocalsZeroed - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal override bool IsRecord => false; internal override bool IsRecordStruct => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 80a611811c5a2..625b693891276 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -111,7 +111,7 @@ internal override SyntaxNode ScopeDesignatorOpt get { return _scopeBinder.ScopeDesignator; } } - // From https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md: + // From https://github.com/dotnet/csharplang/blob/main/csharp-11.0/proposals/low-level-struct-improvements.md: // // | Parameter or Local | ref-safe-to-escape | safe-to-escape | // |------------------------|--------------------|----------------| @@ -208,11 +208,9 @@ public static SourceLocalSymbol MakeDeconstructionLocal( Debug.Assert(closestTypeSyntax != null); Debug.Assert(nodeBinder != null); - Debug.Assert(closestTypeSyntax.Kind() != SyntaxKind.RefType); - // https://github.com/dotnet/roslyn/issues/62039: Allow 'scoped' modifier. - return closestTypeSyntax.IsVar + return closestTypeSyntax.SkipScoped(out _).SkipRef(out _).IsVar ? new DeconstructionLocalSymbol(containingSymbol, scopeBinder, nodeBinder, closestTypeSyntax, identifierToken, kind, deconstruction) - : new SourceLocalSymbol(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: false, closestTypeSyntax, identifierToken, kind); + : new SourceLocalSymbol(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: true, closestTypeSyntax, identifierToken, kind); } /// @@ -241,10 +239,9 @@ internal static LocalSymbol MakeLocalSymbolWithEnclosingContext( Contains(nodeToBind.Ancestors().OfType().First().Kind()) || nodeToBind is ExpressionSyntax); Debug.Assert(!(nodeToBind.Kind() == SyntaxKind.SwitchExpressionArm) || nodeBinder is SwitchExpressionArmBinder); - // https://github.com/dotnet/roslyn/issues/62039: Allow 'scoped' modifier. - return typeSyntax?.IsVar != false && kind != LocalDeclarationKind.DeclarationExpressionVariable + return typeSyntax?.SkipScoped(out _).SkipRef(out _).IsVar != false && kind != LocalDeclarationKind.DeclarationExpressionVariable ? new LocalSymbolWithEnclosingContext(containingSymbol, scopeBinder, nodeBinder, typeSyntax, identifierToken, kind, nodeToBind, forbiddenZone) - : new SourceLocalSymbol(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: false, typeSyntax, identifierToken, kind); + : new SourceLocalSymbol(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: true, typeSyntax, identifierToken, kind); } /// @@ -298,7 +295,7 @@ internal override SynthesizedLocalKind SynthesizedKind internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsPinned @@ -741,7 +738,7 @@ public DeconstructionLocalSymbol( SyntaxToken identifierToken, LocalDeclarationKind declarationKind, SyntaxNode deconstruction) - : base(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: false, typeSyntax, identifierToken, declarationKind) // https://github.com/dotnet/roslyn/issues/62039: Allow 'scoped' modifier. + : base(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: true, typeSyntax, identifierToken, declarationKind) { _deconstruction = deconstruction; _nodeBinder = nodeBinder; @@ -806,7 +803,7 @@ public LocalSymbolWithEnclosingContext( LocalDeclarationKind declarationKind, SyntaxNode nodeToBind, SyntaxNode forbiddenZone) - : base(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: false, typeSyntax, identifierToken, declarationKind) // https://github.com/dotnet/roslyn/issues/62039: Allow 'scoped' modifier. + : base(containingSymbol, scopeBinder, allowRefKind: false, allowScoped: true, typeSyntax, identifierToken, declarationKind) { Debug.Assert( nodeToBind.Kind() == SyntaxKind.CasePatternSwitchLabel || diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 38f0f14a27c84..ce50612f17d11 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -669,7 +669,7 @@ internal override void ForceComplete(SourceLocation? locationOpt, CancellationTo state.SpinWaitComplete(incompletePart, cancellationToken); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal void EnsureFieldDefinitionsNoted() @@ -1079,7 +1079,7 @@ internal int CalculateSyntaxOffsetInSynthesizedConstructor(int position, SyntaxT aggregateLength += syntaxRef.Span.Length; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } int syntaxOffset; @@ -1099,7 +1099,7 @@ internal int CalculateSyntaxOffsetInSynthesizedConstructor(int position, SyntaxT } // an implicit constructor has no body and no initializer, so the variable has to be declared in a member initializer - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index d891b80601ce7..ba89f477d9739 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -1007,7 +1007,7 @@ void checkSingleOverriddenMember(Symbol overridingMember, Symbol overriddenMembe } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } else @@ -1383,7 +1383,7 @@ internal static bool RequiresValidScopedOverrideForRefSafety(MethodSymbol? metho var parameters = method.Parameters; - // https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#scoped-mismatch + // https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/low-level-struct-improvements.md#scoped-mismatch // The compiler will report a diagnostic for _unsafe scoped mismatches_ across overrides, interface implementations, and delegate conversions when: // - The method returns a `ref struct` or returns a `ref` or `ref readonly`, or the method has a `ref` or `out` parameter of `ref struct` type, and // ... @@ -1422,7 +1422,7 @@ internal static bool RequiresValidScopedOverrideForRefSafety(MethodSymbol? metho /// internal static bool ReportInvalidScopedOverrideAsError(MethodSymbol baseMethod, MethodSymbol overrideMethod) { - // https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#scoped-mismatch + // https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/low-level-struct-improvements.md#scoped-mismatch // The diagnostic is reported as an error if the mismatched signatures are both using C#11 ref safety rules; otherwise, the diagnostic is a warning. return baseMethod.UseUpdatedEscapeRules && overrideMethod.UseUpdatedEscapeRules; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 16e6956248e7e..56f4c4dd46fcc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -507,8 +507,8 @@ private TypeAndRefKind GetTypeAndRefKind(ConsList fieldsBeingBound) else { bool isVar; - type = binder.BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar); - + type = binder.BindTypeOrVarKeyword(typeSyntax.SkipScoped(out _).SkipRef(out RefKind refKindToAssert), diagnostics, out isVar); + Debug.Assert(refKindToAssert == RefKind.None); // Otherwise we might need to report an error Debug.Assert(type.HasType || isVar); if (isVar) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs index ed67b11609f51..3ba7ce1b03166 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs @@ -80,7 +80,7 @@ static Location getLocation(ParameterSymbol parameter, Location location) => parameter.Locations.FirstOrDefault() ?? location; } - protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool UseUpdatedEscapeRules => ContainingModule.UseUpdatedEscapeRules; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs index 3c43ab7020c94..61d31e253bad0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs @@ -81,7 +81,7 @@ private SingleNamespaceDeclaration GetMatchingNamespaceDeclaration(CSharpSyntaxN } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #if DEBUG diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index d91e89468575b..1a605bfe97e5d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -25,7 +25,7 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase private readonly string _name; private readonly ImmutableArray _locations; private readonly RefKind _refKind; - private readonly DeclarationScope _scope; + private readonly DeclarationScope? _scope; public static SourceParameterSymbol Create( Binder context, @@ -101,7 +101,7 @@ protected SourceParameterSymbol( TypeWithAnnotations parameterType, int ordinal, RefKind refKind, - DeclarationScope scope, + DeclarationScope? scope, string name, ImmutableArray locations) : base(owner, ordinal) @@ -225,13 +225,16 @@ public sealed override RefKind RefKind /// /// The declared scope. From source, this is from the scope keyword only. /// - internal DeclarationScope DeclaredScope => _scope; + internal DeclarationScope? DeclaredScope => _scope; internal abstract override DeclarationScope EffectiveScope { get; } protected DeclarationScope CalculateEffectiveScopeIgnoringAttributes() { - var declaredScope = this.DeclaredScope; + // DeclaredScope is only null in LambdaParameterSymbol where EffectiveScope can be provided rather than computed + Debug.Assert(this.DeclaredScope is not null); + var declaredScope = this.DeclaredScope.Value; + return declaredScope == DeclarationScope.Unscoped && ParameterHelpers.IsRefScopedByDefault(this) ? DeclarationScope.RefScoped : declaredScope; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 4f8bf1b9ce4dd..338f27a650ef2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -243,7 +243,7 @@ private static AccessorDeclarationSyntax GetGetAccessorDeclaration(BasePropertyD } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static AccessorDeclarationSyntax GetSetAccessorDeclaration(BasePropertyDeclarationSyntax syntax) @@ -258,7 +258,7 @@ private static AccessorDeclarationSyntax GetSetAccessorDeclaration(BasePropertyD } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static DeclarationModifiers MakeModifiers( diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 57db730c5070a..d299ab3050349 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -310,7 +310,7 @@ protected sealed override MethodSymbol FindExplicitlyImplementedMethod(BindingDi break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return this.FindExplicitlyImplementedMethod(isOperator: true, _explicitInterfaceType, interfaceMethodName, explicitInterfaceSpecifier, diagnostics); @@ -827,7 +827,7 @@ protected sealed override void CheckConstraintsForExplicitInterfaceType(Conversi break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(name), diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index de2b9d597ad0a..d6c6955fd67cd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -353,10 +353,10 @@ private ImmutableArray SubstituteParameters() internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); private int ComputeHashCode() { diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs index 212abcdbca0e8..66986d778a6ff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs @@ -156,7 +156,7 @@ internal sealed override ImmutableArray InterfacesNoUseSiteDiag internal override ImmutableArray GetInterfacesToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal abstract override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes); @@ -352,7 +352,7 @@ void cacheResult(ImmutableArray result) internal override IEnumerable GetFieldsToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetEarlyAttributeDecodingMembers() @@ -410,27 +410,27 @@ internal sealed override NamedTypeSymbol ComImportCoClass internal override IEnumerable GetMethodsToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override IEnumerable GetEventsToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override IEnumerable GetPropertiesToEmit() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override IEnumerable GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override FileIdentifier? AssociatedFileIdentifier => _underlyingType.AssociatedFileIdentifier; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index 12e1c07526559..a3b94596e7a2e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -389,17 +389,17 @@ internal bool LoadAndValidateAttributes( Debug.Assert(!earlyDecodingOnly); - this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData); - - removeObsoleteDiagnosticsForForwardedTypes(boundAttributes, attributesToBind, ref diagnostics); - Debug.Assert(diagnostics.DiagnosticBag is not null); - // Store attributes into the bag. bool lazyAttributesStoredOnThisThread = false; if (lazyCustomAttributesBag.SetAttributes(boundAttributes)) { if (attributeMatchesOpt is null) { + this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData); + + removeObsoleteDiagnosticsForForwardedTypes(boundAttributes, attributesToBind, ref diagnostics); + Debug.Assert(diagnostics.DiagnosticBag is not null); + this.RecordPresenceOfBadAttributes(boundAttributes); if (totalAttributesCount != 0) @@ -602,7 +602,7 @@ private ImmutableArray GetAttributesToBind( var binder = rootBinderOpt ?? compilation.GetBinderFactory(syntaxTree).GetBinder(attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); - Debug.Assert(!binder.InAttributeArgument || this is MethodSymbol { MethodKind: MethodKind.LambdaMethod }, "Possible cycle in attribute binding"); + Debug.Assert(!binder.InAttributeArgument || this is MethodSymbol { MethodKind: MethodKind.LambdaMethod or MethodKind.LocalFunction }, "Possible cycle in attribute binding"); for (int i = 0; i < attributesToBindCount - prevCount; i++) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 8ebdaada145a8..3ae0159c5f0df 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -496,6 +496,16 @@ internal static string LambdaCopyParameterName(int ordinal) return ""; } + internal static string AnonymousDelegateParameterName(int index, int parameterCount) + { + // SPEC: parameter names arg1, ..., argn or arg if a single parameter + if (parameterCount == 1) + { + return "arg"; + } + return "arg" + StringExtensions.GetNumeral(index + 1); + } + internal static string MakeFileTypeMetadataNamePrefix(string filePath, ImmutableArray checksumOpt) { var pooledBuilder = PooledStringBuilder.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs index 2c1c034cff8b4..a5493d5cb5988 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs @@ -134,7 +134,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordDeconstruct.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordDeconstruct.cs index b4d1c99232f5d..4620762938da4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordDeconstruct.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordDeconstruct.cs @@ -77,7 +77,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, { PropertySymbol property => property.Type, FieldSymbol field => field.Type, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; if (!parameter.Type.Equals(type, TypeCompareKind.AllIgnoreOptions)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index 0b283fb42d306..92bebf9104c48 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -68,7 +68,7 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override (TypeWithAnnotations Type, ImmutableArray Parameters) MakeParametersAndBindType(BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index 2aabb978ff72c..e8372fb269453 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -75,7 +75,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal ImmutableArray ConstructedFromTypeParameters => _constructedFromTypeParameters; @@ -189,7 +189,7 @@ internal override IEnumerable GetFieldsToEmit() internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override AttributeUsageInfo GetAttributeUsageInfo() => default(AttributeUsageInfo); @@ -198,7 +198,7 @@ internal override IEnumerable GetFieldsToEmit() internal override bool HasSpecialName => false; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs index de22eb3fadbd1..f516e3bbfb3e7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs @@ -36,7 +36,9 @@ internal SynthesizedDelegateInvokeMethod(NamedTypeSymbol containingType, ArrayBu { _containingType = containingType; - Parameters = parameterDescriptions.SelectAsArrayWithIndex((p, i, m) => SynthesizedParameterSymbol.Create(m, p.Type, i, p.RefKind, scope: p.Scope), this); + Parameters = parameterDescriptions.SelectAsArrayWithIndex(static (p, i, a) => + SynthesizedParameterSymbol.Create(a.Method, p.Type, i, p.RefKind, GeneratedNames.AnonymousDelegateParameterName(i, a.ParameterCount), p.Scope), + (Method: this, ParameterCount: parameterDescriptions.Count)); ReturnTypeWithAnnotations = returnType; RefKind = refKind; } @@ -101,7 +103,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation @@ -234,6 +236,6 @@ public override bool IsExtern get { return false; } } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index a6c64fc5d987d..7e0e82cf48c51 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -129,7 +129,7 @@ public SynthesizedEmbeddedAttributeSymbolBase( internal override ObsoleteAttributeData ObsoleteAttributeData => null; - protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable; + protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable(); public override ImmutableArray GetMembers() => Constructors.CastArray(); @@ -186,7 +186,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r } } - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 6dc7bb5c9eaf8..e69f0c4e5eba5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -269,7 +269,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() @@ -279,7 +279,7 @@ internal sealed override ImmutableArray GetAppliedConditionalSymbols() internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static CSharpSyntaxNode DummySyntax() @@ -307,7 +307,7 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE { WasCompilerGenerated = true }; } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 7a7f75f41ee6f..873180ce4361c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -138,7 +138,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ObsoleteAttributeData ObsoleteAttributeData @@ -331,12 +331,12 @@ internal override bool SynthesizesLoweredBoundBody internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override bool IsNullableAnalysisEnabled() => false; - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs index eeecc9bf23d90..56097a6648e17 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs @@ -258,7 +258,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetAppliedConditionalSymbols() @@ -266,6 +266,6 @@ internal override ImmutableArray GetAppliedConditionalSymbols() return ImmutableArray.Empty; } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs index f2aed4437af26..c3bf55e2bd866 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs @@ -116,7 +116,7 @@ internal sealed override bool HasDeclarativeSecurity internal sealed override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ImmutableArray GetAppliedConditionalSymbols() diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs index cd57dfb4357e5..28ed8edcf2b0d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs @@ -68,7 +68,7 @@ internal sealed override ObsoleteAttributeData ObsoleteAttributeData internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsDeclaredReadOnly => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs index adcd799f7338a..b182525234b3e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs @@ -217,7 +217,7 @@ internal override ImmutableArray GetAppliedConditionalSymbols() internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) @@ -276,6 +276,6 @@ private static void CalculateReturnType( returnType = taskT.Construct(resultType); } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index 35df5f8a2e3fe..3408a1512a63e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -157,7 +157,7 @@ public override DllImportData GetDllImportData() public override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override IEnumerable GetSecurityInformation() @@ -409,12 +409,12 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override bool IsNullableAnalysisEnabled() => false; - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs index e007132b8be9f..7f67767ebc761 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs @@ -159,13 +159,13 @@ internal sealed override bool IsCompilerGenerated /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable; + internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable(); /// /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal sealed override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; + internal sealed override uint RefEscapeScope => throw ExceptionUtilities.Unreachable(); internal sealed override DeclarationScope Scope => DeclarationScope.Unscoped; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index b28ebcc1fcae9..4b057e56813e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -82,27 +82,27 @@ internal override ConstantValue? ExplicitDefaultConstantValue internal override bool IsIDispatchConstant { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsIUnknownConstant { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsCallerLineNumber { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsCallerFilePath { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsCallerMemberName { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override int CallerArgumentExpressionParameterIndex diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs index 2f3a74a68203a..a6380e8ce78e7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs @@ -350,7 +350,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetAppliedConditionalSymbols() diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index 4b095d836079c..4cfb133a4d306 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -357,7 +357,7 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override ObsoleteAttributeData? ObsoleteAttributeData @@ -429,7 +429,7 @@ private bool CalculateShouldEmit(ImmutableArray boundInitializ return false; } - protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs index 3c1dec8aa9f1d..afc7a339dba36 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs @@ -102,13 +102,13 @@ public override RefKind RefKind /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable; + internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable(); /// /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; + internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable(); /// /// Compiler should always be synthesizing locals with correct escape semantics. diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs index 65ccbac349bc6..300d578474357 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs @@ -46,7 +46,7 @@ internal static NamedTypeSymbol CreateTuple( if (numElements <= 1) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } NamedTypeSymbol underlyingType = getTupleUnderlyingType(elementTypesWithAnnotations, syntax, compilation, diagnostics); @@ -316,7 +316,7 @@ private static WellKnownType GetTupleType(int arity) { if (arity > ValueTupleRestPosition) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return tupleTypes[arity - 1]; } @@ -344,7 +344,7 @@ internal static WellKnownMember GetTupleCtor(int arity) { if (arity > 8) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return tupleCtors[arity - 1]; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index a95d909debd75..f709ee8db15e3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -2391,7 +2391,7 @@ private ImmutableHashSet ComputeAbstractMembers() [Obsolete("Use TypeWithAnnotations.Is method.", true)] internal bool Equals(TypeWithAnnotations other) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #nullable enable @@ -2408,27 +2408,27 @@ public static bool Equals(TypeSymbol? left, TypeSymbol? right, TypeCompareKind c [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator ==(TypeSymbol left, TypeSymbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator !=(TypeSymbol left, TypeSymbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator ==(Symbol left, TypeSymbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator !=(Symbol left, TypeSymbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator ==(TypeSymbol left, Symbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); [Obsolete("Use 'TypeSymbol.Equals(TypeSymbol, TypeSymbol, TypeCompareKind)' method.", true)] public static bool operator !=(TypeSymbol left, Symbol right) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal ITypeSymbol GetITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) { diff --git a/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs b/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs index 5ab454f94ae31..df9f0c4c43e66 100644 --- a/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs +++ b/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs @@ -97,7 +97,7 @@ internal override ImmutableBindingDiagnostic GetConstantValueDia internal override SyntaxNode GetDeclaratorSyntax() => _underlyingLocal.GetDeclaratorSyntax(); internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) => - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); #endregion } } diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs index 8fed47ddfd464..072297d346871 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs @@ -217,7 +217,7 @@ public override LineVisibility GetLineVisibility(SourceText sourceText, int posi // C# does not have unknown visibility state protected override LineVisibility GetUnknownStateVisibility(int index) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal override FileLinePositionSpan TranslateSpanAndVisibility(SourceText sourceText, string treeFilePath, TextSpan span, out bool isHiddenPosition) { diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs index 4bafba9f572b3..f1719695e36d0 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 // 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; break; } @@ -466,7 +468,7 @@ internal SyntaxToken FindTokenIncludingCrefAndNameAttributes(int position) /// protected override bool EquivalentToCore(SyntaxNode other) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override SyntaxTree SyntaxTreeCore @@ -553,5 +555,31 @@ string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { return ToString(); } + + /// + /// This is ONLY used for debugging purpose + /// + internal string Dump() + { + return TreeDumper.DumpCompact(makeTree(this)); + + static TreeDumperNode makeTree(SyntaxNodeOrToken nodeOrToken) + { + var kind = nodeOrToken.Kind().ToString(); + + if (nodeOrToken.AsNode(out var node) + && node is not IdentifierNameSyntax) + { + return new TreeDumperNode(kind, null, node.ChildNodesAndTokens().Select(makeTree)); + } + + return new TreeDumperNode($"""{kind} {stringOrMissing(nodeOrToken)}"""); + } + + static string stringOrMissing(SyntaxNodeOrToken nodeOrToken) + { + return nodeOrToken.IsMissing ? "" : $@"""{nodeOrToken}"""; + } + } } } 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/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs index a101c1ccf0e6e..85f2617d57600 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; } @@ -56,7 +58,7 @@ public override CSharpParseOptions Options [Obsolete("Obsolete due to performance problems, use CompilationOptions.SyntaxTreeOptionsProvider instead", error: false)] public override ImmutableDictionary DiagnosticOptions - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override string FilePath { @@ -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, ChecksumAlgorithm); public override SyntaxTree WithFilePath(string path) - { - return SyntaxFactory.SyntaxTree(_node, options: this.Options, path: path, encoding: null); - } + => 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 ae622d649cd75..57cf65515236a 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. /// @@ -438,7 +457,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 +934,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)] @@ -928,5 +947,12 @@ public static SyntaxTree Create( ImmutableDictionary? diagnosticOptions) => Create(root, options, path, encoding, diagnosticOptions, isGeneratedCode: null); + /// + /// This is ONLY used for debugging purpose + /// + internal string Dump() + { + return this.GetRoot().Dump(); + } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxToken.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxToken.cs index 1b187921ae9bc..3760bf9c5cd84 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxToken.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxToken.cs @@ -80,7 +80,7 @@ internal SyntaxToken(ObjectReader reader) internal override GreenNode GetSlot(int index) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal static SyntaxToken Create(SyntaxKind kind) @@ -474,7 +474,7 @@ public override bool IsEquivalentTo(GreenNode other) internal override SyntaxNode CreateRed(SyntaxNode parent, int position) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxTrivia.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxTrivia.cs index 6d51e91435eeb..6022af50970a7 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxTrivia.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxTrivia.cs @@ -61,7 +61,7 @@ public override string ToString() internal override GreenNode GetSlot(int index) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int Width @@ -130,7 +130,7 @@ public override bool IsEquivalentTo(GreenNode? other) internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index a2e2e72a2d5b5..64036075b1d79 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1544,13 +1544,12 @@ 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) - { - return CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions?)options, path, encoding); - } + => 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 RS0030 // Do not used banned APIs +#pragma warning disable CS0618 // Type or member is obsolete /// public static SyntaxTree ParseSyntaxTree( string text, @@ -1559,8 +1558,9 @@ 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 /// public static SyntaxTree ParseSyntaxTree( diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index 35725c221ea35..f54710372b369 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -97,7 +97,7 @@ public static bool IsInTypeOnlyContext(ExpressionSyntax node) case FunctionPointerType: // FunctionPointerTypeSyntax has no direct children that are ExpressionSyntaxes - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); case PredefinedType: return true; @@ -448,8 +448,8 @@ internal static bool IsIdentifierVarOrPredefinedType(this Syntax.InternalSyntax. internal static bool IsDeclarationExpressionType(SyntaxNode node, [NotNullWhen(true)] out DeclarationExpressionSyntax? parent) { - parent = node.Parent as DeclarationExpressionSyntax; - return node == parent?.Type; + parent = node.ModifyingScopedOrRefTypeOrSelf().Parent as DeclarationExpressionSyntax; + return node == parent?.Type.SkipScoped(out _).SkipRef(out _); } /// diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index f23d077149281..fd12f93560824 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -255,6 +255,24 @@ internal static TypeSyntax SkipScoped(this TypeSyntax syntax, out bool isScoped) return syntax; } + internal static SyntaxNode ModifyingScopedOrRefTypeOrSelf(this SyntaxNode syntax) + { + SyntaxNode? parentNode = syntax.Parent; + + if (parentNode is RefTypeSyntax refType && refType.Type == syntax) + { + syntax = refType; + parentNode = parentNode.Parent; + } + + if (parentNode is ScopedTypeSyntax scopedType && scopedType.Type == syntax) + { + return scopedType; + } + + return syntax; + } + internal static ExpressionSyntax? CheckAndUnwrapRefExpression( this ExpressionSyntax? syntax, BindingDiagnosticBag diagnostics, diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs index bc11ba11c686d..d547cdf842d27 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs @@ -64,7 +64,7 @@ ConstantValue IValueSet.Sample return tc.ToConstantValue(value); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 516549d490b07..b2086c4938317 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -362,6 +362,11 @@ Název {0} neodpovídá příslušnému parametru Deconstruct {1}. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. Omezení default je platné jen v přepsaných metodách a metodách explicitní implementace rozhraní. @@ -1292,6 +1297,11 @@ Není možné znovu přiřadit {1} k {0}, protože {1} může opustit aktuální metodu pouze prostřednictvím příkazu return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Pole ref nemůže odkazovat na hodnotu ref struct. @@ -1412,6 +1422,11 @@ Cílový modul runtime nepodporuje rozšiřitelné konvence volání ani konvence volání výchozí pro prostředí modulu runtime. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Modifikátor scoped parametru {0} neodpovídá přepsanému nebo implementovanému členu. @@ -2157,6 +2172,16 @@ Tento příkaz přiřazuje hodnotu, která může opustit aktuální metodu pouze prostřednictvím příkazu return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Tato funkce vrací místní {0} podle odkazu, ale nejedná se o místní odkaz @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Vlastnost nebo indexer nejde předat jako parametr ref nebo out. + A non ref-returning property or indexer may not be used as an out or ref value + Vlastnost nebo indexer nejde předat jako parametr ref nebo out. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index bef87b4ee3870..7b5c7556dc23d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -362,6 +362,11 @@ Der Name "{0}" stimmt nicht mit dem entsprechenden Deconstruct-Parameter "{1}" überein. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. Die default-Einschränkung ist nur für Überschreibungsmethoden und Methoden zur expliziten Schnittstellenimplementierung gültig. @@ -1292,6 +1297,11 @@ Eine ref-Zuweisung von "{1}" zu "{0}" ist nicht möglich, weil "{1}" die aktuelle Methode nur über eine return-Anweisung escapen kann. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Ein Ref-Feld kann nicht auf eine Ref-Struktur verweisen. @@ -1412,6 +1422,11 @@ Die Zielruntime unterstützt keine erweiterbaren Aufrufkonventionen oder Standardaufrufkonventionen der Runtime-Umgebung. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Der Modifikator ‚Scoped‘ des Parameters ‚{0}‘ stimmt nicht mit dem überschriebenen oder implementierten Member überein. @@ -2157,6 +2172,16 @@ Diese ref-Zuweisung weist einen Wert zu, der die aktuelle Methode nur über eine return-Anweisung escapen kann. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Gibt die lokale Variable "{0}" als Verweis zurück, es handelt sich jedoch nicht um eine lokale ref-Variable. @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Eine Eigenschaft oder ein Indexer kann nicht als out- oder ref-Parameter übergeben werden. + A non ref-returning property or indexer may not be used as an out or ref value + Eine Eigenschaft oder ein Indexer kann nicht als out- oder ref-Parameter übergeben werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index ac4904a9b63e3..6612728e2812a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -362,6 +362,11 @@ El nombre "{0}" no coincide con el parámetro de "Deconstruct" correspondiente, "{1}". + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. La restricción "default" solo es válida en los métodos de invalidación y de implementación de interfaz explícita. @@ -1292,6 +1297,11 @@ No se puede asignar la referencia "{1}" a "{0}", porque "{1}" solo puede escapar del método actual mediante una instrucción "return". + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Un campo de referencia no puede hacer referencia a una estructura de referencia. @@ -1412,6 +1422,11 @@ El entorno de ejecución de destino no admite convenciones de llamada predeterminadas de entorno en tiempo de ejecución o extensible. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. El modificador 'scoped' del parámetro '{0}' no coincide con el miembro invalidado o implementado. @@ -2157,6 +2172,16 @@ Esta referencia asigna un valor que solo puede escapar del método actual mediante una instrucción "return". + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Esto devuelve por referencia "{0}" de la variable local, pero no es una variable local de tipo ref @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Una propiedad o un indizador no se puede pasar como parámetro out o ref + A non ref-returning property or indexer may not be used as an out or ref value + Una propiedad o un indizador no se puede pasar como parámetro out o ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 39c443c7b8086..0264bd40fe1e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -362,6 +362,11 @@ Le nom '{0}' ne correspond pas au paramètre 'Deconstruct' correspondant '{1}'. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. La contrainte 'default' est uniquement valide sur les méthodes de substitution et d'implémentation d'interface explicite. @@ -1292,6 +1297,11 @@ Cette référence ne peut pas effectuer une attribution par référence '{1}' à '{0}', car '{1}' ne peut échapper à la méthode actuelle qu’à l’aide d’une instruction return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Un champ ref ne peut pas faire référence à un struct ref. @@ -1412,6 +1422,11 @@ Le runtime cible ne prend pas en charge les conventions d'appel par défaut des environnements extensibles ou d'exécution. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Le modificateur 'scoped' du paramètre '{0}' ne correspond pas au membre substitué ou implémenté. @@ -2157,6 +2172,16 @@ Cette référence effectue une valeur qui ne peut échapper à la méthode actuelle qu’à l’aide d’une instruction return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Retourne une variable locale '{0}' par référence, mais il ne s'agit pas d'une variable locale de référence @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Impossible de passer une propriété ou un indexeur en tant que paramètre de sortie (out) ni de référence (ref) + A non ref-returning property or indexer may not be used as an out or ref value + Impossible de passer une propriété ou un indexeur en tant que paramètre de sortie (out) ni de référence (ref) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index d1db246c53d01..f5813fd2a1641 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -362,6 +362,11 @@ Il nome '{0}' non corrisponde al parametro '{1}' di 'Deconstruct' corrispondente. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. Il vincolo 'default' è valido solo in metodi di override e di implementazione esplicita dell'interfaccia. @@ -1292,6 +1297,11 @@ Non è possibile assegnare un riferimento '{1}' a '{0}' perché '{1}' può eseguire l'escape solo del metodo corrente tramite un'istruzione return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Un campo ref non può fare riferimento a un ref struct. @@ -1412,6 +1422,11 @@ Il runtime di destinazione non supporta convenzioni di chiamata predefinite estendibili o dell'ambiente di runtime. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Il modificatore 'scoped' del parametro '{0}' non corrisponde al membro sottoposto a override o implementato. @@ -2157,6 +2172,16 @@ In questo modo viene assegnato un valore che può eseguire l'escape del metodo corrente solo tramite un'istruzione return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local In questo modo viene restituito il valore locale '{0}' per riferimento, ma non è un riferimento locale @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Una proprietà o un indicizzatore non può essere passato come parametro out o ref + A non ref-returning property or indexer may not be used as an out or ref value + Una proprietà o un indicizzatore non può essere passato come parametro out o ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 1007837d98407..470eae4d9027c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -362,6 +362,11 @@ 名前 '{0}' は対応する 'Deconstruct' パラメーター '{1}' と一致しません。 + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. 'default' 制約は、オーバーライドおよび明示的なインターフェイスの実装メソッドでのみ有効です。 @@ -1292,6 +1297,11 @@ '{1}' は return ステートメントを介してのみ現在のメソッドをエスケープできるため、'{1}' を '{0}' に ref 割り当てすることはできません。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. ref フィールドは ref 構造体を参照できません。 @@ -1412,6 +1422,11 @@ ターゲット ランタイムは、拡張可能またはランタイム環境の既定の呼び出し規則をサポートしていません。 + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. パラメーター '{0}' の 'scoped' 修飾子が、オーバーライドされたメンバーまたは実装されたメンバーと一致しません。 @@ -2157,6 +2172,16 @@ これは、return ステートメントを介してのみ現在のメソッドをエスケープできる値を ref 割り当てします。 + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local これは、ローカル変数 '{0}' を参照渡しで返しますが、ref ローカル変数ではありません @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - プロパティまたはインデクサーを out か ref のパラメーターとして渡すことはできません + A non ref-returning property or indexer may not be used as an out or ref value + プロパティまたはインデクサーを out か ref のパラメーターとして渡すことはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 88435c623e945..d92f1f46c2024 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -362,6 +362,11 @@ '{0}' 이름이 해당 'Deconstruct' 매개 변수 '{1}'과(와) 일치하지 않습니다. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. 'default' 제약 조건은 재정의 및 명시적 인터페이스 구현 메서드에만 유효합니다. @@ -1292,6 +1297,11 @@ '{1}'은(는) return 문을 통해서만 현재 메서드를 이스케이프할 수 있으므로 '{1}'을(를) '{0}'에 다시 할당할 수 없습니다. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. ref 필드는 ref 구조체를 참조할 수 없습니다. @@ -1412,6 +1422,11 @@ 대상 런타임에서 확장 가능 또는 런타임 환경 기본 호출 규칙을 지원하지 않습니다. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. 매개 변수 '{0}'의 '범위' 수정자가 재정의되거나 구현된 멤버와 일치하지 않습니다. @@ -2157,6 +2172,16 @@ 이 ref는 return 문을 통해서만 현재 메서드를 이스케이프할 수 있는 값을 할당합니다. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local 참조로 로컬 '{0}'을(를) 반환하지만 참조 로컬이 아닙니다. @@ -5236,8 +5261,8 @@ - A property or indexer may not be passed as an out or ref parameter - 속성 또는 인덱서는 out 또는 ref 매개 변수로 전달할 수 없습니다. + A non ref-returning property or indexer may not be used as an out or ref value + 속성 또는 인덱서는 out 또는 ref 매개 변수로 전달할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 0ae63c686053e..24090dd9237e8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -362,6 +362,11 @@ Nazwa „{0}” nie jest zgodna z odpowiednim parametrem „Deconstruct” „{1}”. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. Ograniczenie „default” jest prawidłowe tylko w przypadku ograniczenia dla przesłoniętych i jawnych metod implementacji interfejsu. @@ -1292,6 +1297,11 @@ Nie można przypisać odwołania „{1}” do „{0}”, ponieważ „{1}” może jedynie opuścić bieżącą metodę za pomocą instrukcji return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Pole referencyjne nie może odwoływać się do struktury referencyjnej. @@ -1412,6 +1422,11 @@ Docelowe środowisko uruchomieniowe nie obsługuje rozszerzalnych ani domyślnych dla środowiska uruchomieniowego konwencji wywoływania. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Modyfikator „scoped” parametru „{0}” nie jest zgodny z przesłoniętą lub zaimplementowaną składową. @@ -2157,6 +2172,16 @@ To odwołanie przypisuje wartość, która może tylko pomijać bieżącą metodę za pośrednictwem instrukcji return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Spowoduje to zwrócenie lokalnego elementu „{0}” przez odwołanie, ale nie jest to odwołanie lokalne @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Nie można przekazać właściwości lub indeksatora jako parametru „out” lub „ref”. + A non ref-returning property or indexer may not be used as an out or ref value + Nie można przekazać właściwości lub indeksatora jako parametru „out” lub „ref”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 8640c2566a35e..ba54938a62f00 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -362,6 +362,11 @@ O nome '{0}' não corresponde ao parâmetro 'Deconstruct' '{1}'. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. A restrição 'default' é válida somente nos métodos de substituição e de implementação explícita da interface. @@ -1292,6 +1297,11 @@ Não é possível atribuir '{1}' a '{0}' porque '{1}' só pode escapar do método atual por meio de uma instrução return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Um campo ref não pode se referir a um struct ref. @@ -1412,6 +1422,11 @@ O runtime de destino não dá suporte a convenções de chamada padrão extensíveis ou de ambiente de runtime. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. O modificador 'scoped' do parâmetro '{0}' não corresponde ao membro substituído ou implementado. @@ -2157,6 +2172,16 @@ Essa referência atribui um valor que só pode escapar o método atual por meio de uma instrução return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Isso retorna o local '{0}' por referência, mas não é um local de ref @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Talvez uma propriedade ou um indexador não possa ser passado como um parâmetro out ou ref + A non ref-returning property or indexer may not be used as an out or ref value + Talvez uma propriedade ou um indexador não possa ser passado como um parâmetro out ou ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 996552c994ec2..7f034d5294183 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -362,6 +362,11 @@ Имя "{0}" не соответствует указанному параметру "Deconstruct" "{1}". + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. Ограничение "default" допустимо только для переопределенных и явных методов реализации интерфейса. @@ -1292,6 +1297,11 @@ Не удается присвоить по ссылкеь "{1}" для "{0}",так как "{1}" может избежать текущего метода только через оператор return. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Поле ref не должно ссылаться на ref struct. @@ -1412,6 +1422,11 @@ Целевая среда выполнения не поддерживает расширяемые или принадлежащие среде выполнения соглашения о вызовах по умолчанию. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. Модификатор "scoped" параметра "{0}" не совпадает с переопределенным или реализованным членом. @@ -2157,6 +2172,16 @@ Это присваивает по ссылке значение, которое может избежать текущего метода только через оператор return. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Это возвращает local "{0}" по ссылке, но это не ref local @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Свойство или индексатор не могут передаваться как параметр out или ref. + A non ref-returning property or indexer may not be used as an out or ref value + Свойство или индексатор не могут передаваться как параметр out или ref. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 7509a662e24b1..68078b1ceab59 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -362,6 +362,11 @@ '{0}' adı ilgili '{1}' 'Deconstruct' parametresiyle eşleşmiyor. + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. 'default' kısıtlaması yalnızca geçersiz kılma ve açık arabirim uygulama metotlarında geçerlidir. @@ -1292,6 +1297,11 @@ '{1}', '{0}'ye yeniden atanamaz çünkü '{1}' yalnızca bir return ifadesi aracılığıyla geçerli yöntemden kaçabilir. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. Başvuru alanı bir başvuru yapısına başvuramaz. @@ -1412,6 +1422,11 @@ Hedef çalışma zamanı, genişletilebilir veya çalışma zamanı ortamı varsayılanı çağırma kurallarını desteklemiyor. + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. '{0}' parametresinin 'scoped' değiştiricisi geçersiz kılınan veya uygulanan üyeyle eşleşmiyor. @@ -2157,6 +2172,16 @@ Bu başvuru, yalnızca bir return ifadesi aracılığıyla geçerli yöntemden kaçabilen bir değer atar. + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local Bu, referans olarak yerel '{0}' döndürür, ancak bir ref yerel değildir @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - Bir özellik veya dizin erişimcisi, out veya ref parametresi olarak geçilemez + A non ref-returning property or indexer may not be used as an out or ref value + Bir özellik veya dizin erişimcisi, out veya ref parametresi olarak geçilemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index a39233ccaf0ae..519c96f1b7830 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -362,6 +362,11 @@ 名称“{0}”与相应 "Deconstruct" 参数“{1}”不匹配。 + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. "default" 约束仅针对替代和显式接口实现方法有效。 @@ -1292,6 +1297,11 @@ 无法将“{1}”ref 分配给“{0}”,因为“{1}”只能通过 return 语句对当前方法进行转义。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. ref 字段不能引用 ref 结构。 @@ -1412,6 +1422,11 @@ 目标运行时不支持可扩展或运行时环境默认调用约定。 + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. 参数 "{0}" 的 "scoped" 修饰符与被重写或实现的成员不匹配。 @@ -2157,6 +2172,16 @@ 此 ref 分配的值只能通过 return 语句转义当前方法。 + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local 这将按引用返回本地“{0}”,但它不是 ref 本地 @@ -5242,8 +5267,8 @@ - A property or indexer may not be passed as an out or ref parameter - 属性或索引器不能作为 out 或 ref 参数传递 + A non ref-returning property or indexer may not be used as an out or ref value + 属性或索引器不能作为 out 或 ref 参数传递 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index b46dd1fc3ee63..457d6c2bf9e6e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -362,6 +362,11 @@ 名稱 '{0}' 與對應的 'Deconstruct' 參數 '{1}' 不相符。 + + A deconstruction variable cannot be declared as a ref local + A deconstruction variable cannot be declared as a ref local + + The 'default' constraint is valid on override and explicit interface implementation methods only. 'default' 條件約束只在覆寫和明確介面實作方法上有效。 @@ -1292,6 +1297,11 @@ 無法將 '{1}' 參考指派至 '{0}',因為 '{1}' 只能透過 return 陳述式逸出目前的方法。 + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + Cannot ref-assign '{1}' to '{0}' because '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + A ref field cannot refer to a ref struct. ref 欄位不能參考 ref 結構。 @@ -1412,6 +1422,11 @@ 目標執行階段不支援可延伸或執行階段環境的預設呼叫慣例。 + + The 'scoped' modifier cannot be used with discard. + The 'scoped' modifier cannot be used with discard. + + The 'scoped' modifier of parameter '{0}' doesn't match overridden or implemented member. 參數 '{0}' 的 'scoped' 修飾元不符合覆寫或實作的成員。 @@ -2157,6 +2172,16 @@ 此參考指派的值只能透過 return 陳述式逸出目前的方法。 + + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + This ref-assigns '{1}' to '{0}' but '{1}' has a wider value escape scope than '{0}' allowing assignment through '{0}' of values with narrower escapes scopes than '{1}'. + + + + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + This ref-assigns a value that has a wider value escape scope than the target allowing assignment through the target of values with narrower escapes scopes. + + This returns local '{0}' by reference but it is not a ref local 這會藉傳址方式傳回本機 '{0}',但其非參考本機 @@ -5237,8 +5262,8 @@ - A property or indexer may not be passed as an out or ref parameter - 屬性或索引子不可以 out 或 ref 參數形式傳遞 + A non ref-returning property or indexer may not be used as an out or ref value + 屬性或索引子不可以 out 或 ref 參數形式傳遞 diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 4982f73eaff0e..a4dd2debca748 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -379,7 +379,10 @@ public void RunWithShiftJisFile() [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30321")] public void CompilerBinariesAreAnyCPU() { +#pragma warning disable SYSLIB0037 + // warning SYSLIB0037: 'AssemblyName.ProcessorArchitecture' is obsolete: 'AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete and not supported.' Assert.Equal(ProcessorArchitecture.MSIL, AssemblyName.GetAssemblyName(s_CSharpCompilerExecutable).ProcessorArchitecture); +#pragma warning restore SYSLIB0037 } [Fact] @@ -8615,7 +8618,7 @@ public void FileShareDeleteCompatibility_Windows() fsDll.Dispose(); fsPdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); } /// @@ -8672,7 +8675,7 @@ public void FileShareDeleteCompatibility_Xplat() peDll.Dispose(); pePdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); // files can be deleted now: File.Delete(libSrc.Path); @@ -8713,7 +8716,7 @@ public void FileShareDeleteCompatibility_ReadOnlyFiles() fsDll.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); } [Fact] diff --git a/src/Compilers/CSharp/Test/CommandLine/Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests.csproj b/src/Compilers/CSharp/Test/CommandLine/Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests.csproj index 127bbc17aafc2..43d7fa7d45b14 100644 --- a/src/Compilers/CSharp/Test/CommandLine/Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/CommandLine/Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests - net6.0;net472 + net7.0;net472 true diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index d9b14bb7d5a4d..3fe6229f90f8f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -45,7 +45,7 @@ private CompilationVerifier CompileAndVerifyFunctionPointers( private static CSharpCompilation CreateCompilationWithFunctionPointers(CSharpTestSource source, IEnumerable? references = null, CSharpCompilationOptions? options = null, TargetFramework? targetFramework = null) { - return CreateCompilation(source, references: references, options: options ?? TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: targetFramework ?? TargetFramework.NetCoreApp); + return CreateCompilation(source, references: references, options: options ?? TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: targetFramework ?? TargetFramework.Net50); } private CompilationVerifier CompileAndVerifyFunctionPointersWithIl(string source, string ilStub, Action? symbolValidator = null, string? expectedOutput = null) @@ -56,7 +56,7 @@ private CompilationVerifier CompileAndVerifyFunctionPointersWithIl(string source private static CSharpCompilation CreateCompilationWithFunctionPointersAndIl(string source, string ilStub, IEnumerable? references = null, CSharpCompilationOptions? options = null) { - return CreateCompilationWithIL(source, ilStub, references: references, options: options ?? TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + return CreateCompilationWithIL(source, ilStub, references: references, options: options ?? TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net50); } [Theory] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs index f93d0f329adb6..5ea77d774df7c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs @@ -1128,24 +1128,24 @@ static void M(in int arg1, in (int Alice, int Bob) arg2) var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,9): error CS8408: Cannot assign to variable 'in int' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 = 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "in int").WithLocation(6, 9), - // (7,9): error CS8409: Cannot assign to a member of variable 'in (int Alice, int Bob)' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(6, 9), + // (7,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice = 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(7, 9), - // (9,9): error CS8408: Cannot assign to variable 'in int' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(7, 9), + // (9,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 ++; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "in int").WithLocation(9, 9), - // (10,9): error CS8409: Cannot assign to a member of variable 'in (int Alice, int Bob)' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(9, 9), + // (10,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice --; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(10, 9), - // (12,9): error CS8408: Cannot assign to variable 'in int' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(10, 9), + // (12,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 += 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "in int"), - // (13,9): error CS8409: Cannot assign to a member of variable 'in (int Alice, int Bob)' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(12, 9), + // (13,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice -= 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)")); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(13, 9)); } [Fact] @@ -1180,12 +1180,12 @@ static ref int M2(in int arg1, in (int Alice, int Bob) arg2) var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (18,20): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + // (18,20): error CS8333: Cannot return variable 'arg1' by writable reference because it is a readonly variable // return ref arg1; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "in int").WithLocation(18, 20), - // (23,20): error CS8334: Members of variable 'in (int Alice, int Bob)' cannot be returned by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(18, 20), + // (23,20): error CS8334: Members of variable 'arg2' cannot be returned by writable reference because it is a readonly variable // return ref arg2.Alice; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(23, 20) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(23, 20) ); } @@ -1205,12 +1205,12 @@ static void M(in int arg1, in (int Alice, int Bob) arg2) var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,25): error CS8406: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (6,25): error CS8329: Cannot use variable 'arg1' as a ref or out value because it is a readonly variable // ref var y = ref arg1; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "arg1").WithArguments("variable", "in int"), - // (7,25): error CS8407: Members of variable 'in (int Alice, int Bob)' cannot be used as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(6, 25), + // (7,25): error CS8330: Members of variable 'arg2' cannot be used as a ref or out value because it is a readonly variable // ref int a = ref arg2.Alice; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)")); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(7, 25)); } [WorkItem(22306, "https://github.com/dotnet/roslyn/issues/22306")] @@ -1271,12 +1271,12 @@ static ref int M(in int arg1, in (int Alice, int Bob) arg2) var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (10,24): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + // (10,24): error CS8333: Cannot return variable 'arg1' by writable reference because it is a readonly variable // return ref arg1; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "in int").WithLocation(10, 24), - // (14,24): error CS8334: Members of variable 'in (int Alice, int Bob)' cannot be returned by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(10, 24), + // (14,24): error CS8334: Members of variable 'arg2' cannot be returned by writable reference because it is a readonly variable // return ref arg2.Alice; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(14, 24) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(14, 24) ); } @@ -1390,12 +1390,12 @@ ref int M1(in int arg11, in (int Alice, int Bob) arg21) var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (12,28): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + // (12,28): error CS8333: Cannot return variable 'arg11' by writable reference because it is a readonly variable // return ref arg11; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg11").WithArguments("variable", "in int").WithLocation(12, 28), - // (16,28): error CS8334: Members of variable 'in (int Alice, int Bob)' cannot be returned by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg11").WithArguments("variable", "arg11").WithLocation(12, 28), + // (16,28): error CS8334: Members of variable 'arg21' cannot be returned by writable reference because it is a readonly variable // return ref arg21.Alice; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg21.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(16, 28) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg21.Alice").WithArguments("variable", "arg21").WithLocation(16, 28) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs index a2abf3f716134..02e78c10d25be 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs @@ -963,12 +963,12 @@ public static void Main() Console.WriteLine(c.GetF1() ??= 2); } }").VerifyDiagnostics( - // (24,27): error CS8331: Cannot assign to property 'C.P1' or use it as the right hand side of a ref assignment because it is a readonly variable + // (24,27): error CS8331: Cannot assign to property 'P1' or use it as the right hand side of a ref assignment because it is a readonly variable // Console.WriteLine(c.P1 ??= 1); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.P1").WithArguments("property", "C.P1").WithLocation(24, 27), - // (26,27): error CS8331: Cannot assign to method 'C.GetF1()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.P1").WithArguments("property", "P1").WithLocation(24, 27), + // (26,27): error CS8331: Cannot assign to method 'GetF1' or use it as the right hand side of a ref assignment because it is a readonly variable // Console.WriteLine(c.GetF1() ??= 2); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.GetF1()").WithArguments("method", "C.GetF1()").WithLocation(26, 27) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "c.GetF1()").WithArguments("method", "GetF1").WithLocation(26, 27) ); } @@ -2597,9 +2597,9 @@ void M(in object o1, out object o2) }"; CreateCompilation(source).VerifyDiagnostics( - // (6,9): error CS8331: Cannot assign to variable 'in object' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to variable 'o1' or use it as the right hand side of a ref assignment because it is a readonly variable // o1 ??= null; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "o1").WithArguments("variable", "in object").WithLocation(6, 9), + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "o1").WithArguments("variable", "o1").WithLocation(6, 9), // (7,9): error CS0269: Use of unassigned out parameter 'o2' // o2 ??= null; Diagnostic(ErrorCode.ERR_UseDefViolationOut, "o2").WithArguments("o2").WithLocation(7, 9) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs index 214e58d515937..8cdd9f745d067 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs @@ -542,24 +542,24 @@ static void Test() var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,9): error CS8331: Cannot assign to method 'Program.M()' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to method 'M' or use it as the right hand side of a ref assignment because it is a readonly variable // M() = 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "Program.M()").WithLocation(6, 9), - // (7,9): error CS8332: Cannot assign to a member of method 'Program.M1()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "M").WithLocation(6, 9), + // (7,9): error CS8332: Cannot assign to a member of method 'M1' or use it as the right hand side of a ref assignment because it is a readonly variable // M1().Alice = 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "Program.M1()").WithLocation(7, 9), - // (9,9): error CS8331: Cannot assign to method 'Program.M()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "M1").WithLocation(7, 9), + // (9,9): error CS8331: Cannot assign to method 'M' or use it as the right hand side of a ref assignment because it is a readonly variable // M() ++; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "Program.M()").WithLocation(9, 9), - // (10,9): error CS8332: Cannot assign to a member of method 'Program.M1()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "M").WithLocation(9, 9), + // (10,9): error CS8332: Cannot assign to a member of method 'M1' or use it as the right hand side of a ref assignment because it is a readonly variable // M1().Alice --; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "Program.M1()").WithLocation(10, 9), - // (12,9): error CS8331: Cannot assign to method 'Program.M()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "M1").WithLocation(10, 9), + // (12,9): error CS8331: Cannot assign to method 'M' or use it as the right hand side of a ref assignment because it is a readonly variable // M() += 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "Program.M()").WithLocation(12, 9), - // (13,9): error CS8332: Cannot assign to a member of method 'Program.M1()' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "M()").WithArguments("method", "M").WithLocation(12, 9), + // (13,9): error CS8332: Cannot assign to a member of method 'M1' or use it as the right hand side of a ref assignment because it is a readonly variable // M1().Alice -= 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "Program.M1()").WithLocation(13, 9) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "M1().Alice").WithArguments("method", "M1").WithLocation(13, 9) ); } @@ -588,24 +588,24 @@ static void Test() var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,9): error CS8331: Cannot assign to property 'Program.P' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to property 'P' or use it as the right hand side of a ref assignment because it is a readonly variable // P = 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "Program.P").WithLocation(6, 9), - // (7,9): error CS8332: Cannot assign to a member of property 'Program.P1' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "P").WithLocation(6, 9), + // (7,9): error CS8332: Cannot assign to a member of property 'P1' or use it as the right hand side of a ref assignment because it is a readonly variable // P1.Alice = 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "Program.P1").WithLocation(7, 9), - // (9,9): error CS8331: Cannot assign to property 'Program.P' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "P1").WithLocation(7, 9), + // (9,9): error CS8331: Cannot assign to property 'P' or use it as the right hand side of a ref assignment because it is a readonly variable // P ++; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "Program.P").WithLocation(9, 9), - // (10,9): error CS8332: Cannot assign to a member of property 'Program.P1' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "P").WithLocation(9, 9), + // (10,9): error CS8332: Cannot assign to a member of property 'P1' or use it as the right hand side of a ref assignment because it is a readonly variable // P1.Alice --; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "Program.P1").WithLocation(10, 9), - // (12,9): error CS8331: Cannot assign to property 'Program.P' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "P1").WithLocation(10, 9), + // (12,9): error CS8331: Cannot assign to property 'P' or use it as the right hand side of a ref assignment because it is a readonly variable // P += 1; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "Program.P").WithLocation(12, 9), - // (13,9): error CS8332: Cannot assign to a member of property 'Program.P1' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "P").WithArguments("property", "P").WithLocation(12, 9), + // (13,9): error CS8332: Cannot assign to a member of property 'P1' or use it as the right hand side of a ref assignment because it is a readonly variable // P1.Alice -= 2; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "Program.P1").WithLocation(13, 9) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "P1.Alice").WithArguments("property", "P1").WithLocation(13, 9) ); } @@ -632,18 +632,18 @@ static void Test() var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (6,25): error CS8329: Cannot use method 'Program.M()' as a ref or out value because it is a readonly variable + // (6,25): error CS8329: Cannot use method 'M' as a ref or out value because it is a readonly variable // ref var y = ref M(); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "M()").WithArguments("method", "Program.M()").WithLocation(6, 25), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "M()").WithArguments("method", "M").WithLocation(6, 25), // (7,25): error CS0119: 'Program.M1()' is a method, which is not valid in the given context // ref int a = ref M1.Alice; Diagnostic(ErrorCode.ERR_BadSKunknown, "M1").WithArguments("Program.M1()", "method").WithLocation(7, 25), - // (8,26): error CS8329: Cannot use property 'Program.P' as a ref or out value because it is a readonly variable + // (8,26): error CS8329: Cannot use property 'P' as a ref or out value because it is a readonly variable // ref var y1 = ref P; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "P").WithArguments("property", "Program.P").WithLocation(8, 26), - // (9,26): error CS8330: Members of property 'Program.P1' cannot be used as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "P").WithArguments("property", "P").WithLocation(8, 26), + // (9,26): error CS8330: Members of property 'P1' cannot be used as a ref or out value because it is a readonly variable // ref int a1 = ref P1.Alice; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "P1.Alice").WithArguments("property", "Program.P1").WithLocation(9, 26) + Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "P1.Alice").WithArguments("property", "P1").WithLocation(9, 26) ); } @@ -746,18 +746,18 @@ static ref int Test() var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (12,28): error CS8333: Cannot return method 'Program.M()' by writable reference because it is a readonly variable + // (12,28): error CS8333: Cannot return method 'M' by writable reference because it is a readonly variable // return ref M(); - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "M()").WithArguments("method", "Program.M()").WithLocation(12, 28), - // (16,28): error CS8334: Members of method 'Program.M1()' cannot be returned by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "M()").WithArguments("method", "M").WithLocation(12, 28), + // (16,28): error CS8334: Members of method 'M1' cannot be returned by writable reference because it is a readonly variable // return ref M1().Alice; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "M1().Alice").WithArguments("method", "Program.M1()").WithLocation(16, 28), - // (23,28): error CS8333: Cannot return property 'Program.P' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "M1().Alice").WithArguments("method", "M1").WithLocation(16, 28), + // (23,28): error CS8333: Cannot return property 'P' by writable reference because it is a readonly variable // return ref P; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "P").WithArguments("property", "Program.P").WithLocation(23, 28), - // (27,28): error CS8334: Members of property 'Program.P1' cannot be returned by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "P").WithArguments("property", "P").WithLocation(23, 28), + // (27,28): error CS8334: Members of property 'P1' cannot be returned by writable reference because it is a readonly variable // return ref P1.Alice; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "P1.Alice").WithArguments("property", "Program.P1").WithLocation(27, 28) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "P1.Alice").WithArguments("property", "P1").WithLocation(27, 28) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index a245d123c882b..da0d4ebfa0802 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -29041,5 +29041,67 @@ static void verifyDefaultFieldType(FieldSymbol tupleField, int i, string nullabi tupleField.CorrespondingTupleField.ToTestDisplayString(includeNonNullable: true)); } } + + [Fact] + [WorkItem(64777, "https://github.com/dotnet/roslyn/issues/64777")] + public void NameMismatchInUserDefinedConversion() + { + var source = @" +class C +{ + static void Main() + { + System.Console.WriteLine(""---""); + System.Console.WriteLine(Test1().Property is null); + System.Console.WriteLine(Test2().Property is null); + System.Console.WriteLine(""---""); + } + + static ImplicitConversionTargetType<(int, bool)?> Test1() => ((int, bool)?) null; + static ImplicitConversionTargetType<(int SomeInt, bool SomeBool)?> Test2()=> ((int, bool)?) null; +} + +public class ImplicitConversionTargetType +{ + public T Property { get; } + + public ImplicitConversionTargetType(T property) { Property = property; } + + public static implicit operator ImplicitConversionTargetType(T operand) => new(operand); +} +"; + + var verifier = CompileAndVerify(source + trivial2uple, expectedOutput: +@" +--- +True +True +--- +").VerifyDiagnostics(); + + verifier.VerifyIL("C.Test1", @" +{ + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.ValueTuple? V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""System.ValueTuple?"" + IL_0008: ldloc.0 + IL_0009: call ""ImplicitConversionTargetType?> ImplicitConversionTargetType?>.op_Implicit(System.ValueTuple?)"" + IL_000e: ret +}"); + + verifier.VerifyIL("C.Test2", @" +{ + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.ValueTuple? V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""System.ValueTuple?"" + IL_0008: ldloc.0 + IL_0009: call ""ImplicitConversionTargetType?> ImplicitConversionTargetType?>.op_Implicit(System.ValueTuple?)"" + IL_000e: ret +}"); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index c78fa5d53800e..5afa85e095ecd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -1377,9 +1377,9 @@ void M(C c, in int y) ref readonly int VerifyDelegate(in int y) => throw null; }", comp => comp.VerifyDiagnostics( - // (12,9): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (12,9): error CS8329: Cannot use variable 'y' as a ref or out value because it is a readonly variable // y.R_extension(); // error 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(12, 9), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "y").WithLocation(12, 9), // (13,9): error CS1510: A ref or out value must be an assignable variable // 1.R_extension(); // error 2 Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(13, 9) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index dd46ff9d2e4eb..592775cd58d8b 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -5782,7 +5782,7 @@ interface I interface J { } }"; - var compilation0 = CreateCompilation(source0, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp); + var compilation0 = CreateCompilation(source0, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugDll, targetFramework: TargetFramework.Net50); var compilation1 = compilation0.WithSource(source1); var compilation2 = compilation1.WithSource(source2); diff --git a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj index 238c253b9cb1c..27f2ea81b0670 100644 --- a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.UnitTests - net6.0;net472 + net7.0;net472 true 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/Emit2/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs index a3d711d2703a1..e8cc6908a52d3 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs @@ -714,17 +714,21 @@ void M0() { } Assert.Equal("M0", attrs.Single().ConstructorArguments.Single().Value); var operation = semanticModel.GetOperation(attrSyntax); - // note: this operation tree should contain a constant string "M0" instead of null. - // this should ideally be fixed as part of https://github.com/dotnet/roslyn/issues/53618. VerifyOperationTree(comp, operation, @" -IOperation: (OperationKind.None, Type: Attr) (Syntax: 'Attr()') - Children(1): - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "); } [Fact] - public void TestAttributeCallerInfoSemanticModel_Speculative() + public void TestAttributeCallerInfoSemanticModel_Method_Speculative() { var source = @" using System; @@ -752,12 +756,182 @@ void M0() { } Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); - // note: this operation tree should contain a constant string "M0" instead of null. - // this should ideally be fixed as part of https://github.com/dotnet/roslyn/issues/53618. VerifyOperationTree(comp, speculativeOperation, @" -IOperation: (OperationKind.None, Type: Attr) (Syntax: 'Attr()') - Children(1): - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Method_Speculative2() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +class C +{ + private const string World = ""World""; + + [Attr($""Hello {World}"")] + void M0() { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + var interpolationSyntax = root.DescendantNodes().OfType().Single(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(interpolationSyntax.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Parameter_Speculative() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +class C +{ + void M0([Attr(""a"")] int x) { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M0"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Class_Speculative() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +class Attr : Attribute { public Attr([CallerMemberName] string s = null) { } } + +[Attr(""a"")] +class C +{ +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().Last(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().Last(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.ArgumentList.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = null])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + IDefaultValueOperation (OperationKind.DefaultValue, Type: System.String, Constant: null, IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"); + } + + [Fact] + public void TestAttributeCallerInfoSemanticModel_Speculative_AssemblyTarget() + { + var source = @" +using System; +using System.Runtime.CompilerServices; + +[assembly: Attr(""a"")] + +class Attr : Attribute { public Attr([CallerMemberName] string s = ""default_value"") { } } + +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var root = tree.GetRoot(); + var attrSyntax = root.DescendantNodes().OfType().First(); + + var semanticModel = comp.GetSemanticModel(tree); + var newRoot = root.ReplaceNode(attrSyntax, attrSyntax.WithArgumentList(SyntaxFactory.ParseAttributeArgumentList("()"))); + var newAttrSyntax = newRoot.DescendantNodes().OfType().First(); + + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(attrSyntax.Position, newAttrSyntax, out var speculativeModel)); + + var speculativeOperation = speculativeModel.GetOperation(newAttrSyntax); + VerifyOperationTree(comp, speculativeOperation, @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Attr()') + IObjectCreationOperation (Constructor: Attr..ctor([System.String s = ""default_value""])) (OperationKind.ObjectCreation, Type: Attr, IsImplicit) (Syntax: 'Attr()') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Attr()') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""default_value"", IsImplicit) (Syntax: 'Attr()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "); } diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs index 7167cebecbe11..2c87660e74f32 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -64,7 +64,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { @@ -130,7 +130,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { @@ -195,7 +195,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); compilation.VerifyDiagnostics( // (29,22): error CS8964: The CallerArgumentExpressionAttribute may only be applied to parameters with default values // delegate void D([CallerArgumentExpression(s5)] [Optional] [DefaultParameterValue("default")] ref string s1, string s2, string s3, string s4, string s5); @@ -310,7 +310,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); compilation.VerifyDiagnostics( // (29,33): error CS8964: The CallerArgumentExpressionAttribute may only be applied to parameters with default values // delegate void D(string s1, [CallerArgumentExpression(s1)] [Optional] [DefaultParameterValue("default")] ref string s2); @@ -367,7 +367,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics( // (29,33): warning CS8963: The CallerArgumentExpressionAttribute applied to parameter 's2' will have no effect. It is applied with an invalid parameter name. // delegate void D(string s1, [CallerArgumentExpression(callback)] [Optional] [DefaultParameterValue("default")] string s2); @@ -1436,7 +1436,7 @@ public InterpolatedStringHandlerAttribute() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation, expectedOutput: "1 + /**/ 1").VerifyDiagnostics(); } #endregion @@ -2436,7 +2436,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { // Code size 29 (0x1d) @@ -2487,7 +2487,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { // Code size 29 (0x1d) diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs index 4ed6d2a213243..476388dbfc2b9 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs @@ -38,8 +38,8 @@ public static void F(scoped ref int i) { } }"; var comp = CreateCompilation(new[] { ScopedRefAttributeDefinition, source }); var expected = -@"void Program.F(ref System.Int32 i) - [ScopedRef] ref System.Int32 i +@"void Program.F(scoped ref System.Int32 i) + [ScopedRef] scoped ref System.Int32 i "; CompileAndVerify(comp, symbolValidator: module => { @@ -62,8 +62,8 @@ public static void F(scoped ref int i) { } }"; comp = CreateCompilation(source, references: new[] { ref0 }); var expected = -@"void Program.F(ref System.Int32 i) - [ScopedRef] ref System.Int32 i +@"void Program.F(scoped ref System.Int32 i) + [ScopedRef] scoped ref System.Int32 i "; CompileAndVerify(comp, symbolValidator: module => { @@ -258,22 +258,22 @@ static void Main() Diagnostic(ErrorCode.ERR_BindToBogus, "F2").WithArguments("A.F2(int)").WithLocation(7, 11)); var method = comp.GetMember("A.F1"); - Assert.Equal("void A.F1(scoped R r)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); + Assert.Equal("void A.F1(scoped R r)", method.ToTestDisplayString()); var parameter = method.Parameters[0]; Assert.Equal(DeclarationScope.ValueScoped, parameter.EffectiveScope); method = comp.GetMember("A.F2"); - Assert.Equal("void A.F2(System.Int32 y)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); + Assert.Equal("void A.F2(System.Int32 y)", method.ToTestDisplayString()); parameter = method.Parameters[0]; Assert.Equal(DeclarationScope.Unscoped, parameter.EffectiveScope); method = comp.GetMember("A.F3"); - Assert.Equal("void A.F3(System.Object x, scoped ref System.Int32 y)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); + Assert.Equal("void A.F3(System.Object x, scoped ref System.Int32 y)", method.ToTestDisplayString()); parameter = method.Parameters[1]; Assert.Equal(DeclarationScope.RefScoped, parameter.EffectiveScope); method = comp.GetMember("A.F4"); - Assert.Equal("void A.F4(scoped ref R r)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); + Assert.Equal("void A.F4(scoped ref R r)", method.ToTestDisplayString()); parameter = method.Parameters[0]; Assert.Equal(DeclarationScope.RefScoped, parameter.EffectiveScope); } @@ -338,15 +338,15 @@ public static void F(scoped R r) { } }"; var comp = CreateCompilation(source); var expected = -@"S..ctor(ref System.Int32 i) - [ScopedRef] ref System.Int32 i -void S.F(R r) - [ScopedRef] R r -S S.op_Addition(S a, in R b) +@"S..ctor(scoped ref System.Int32 i) + [ScopedRef] scoped ref System.Int32 i +void S.F(scoped R r) + [ScopedRef] scoped R r +S S.op_Addition(S a, scoped in R b) S a - [ScopedRef] in R b -System.Object S.this[in System.Int32 i].get - [ScopedRef] in System.Int32 i + [ScopedRef] scoped in R b +System.Object S.this[scoped in System.Int32 i].get + [ScopedRef] scoped in System.Int32 i "; CompileAndVerify(comp, symbolValidator: module => { @@ -414,10 +414,11 @@ public static void F5(scoped in R r) { } }"; var comp = CreateCompilation(source); var expected = -@"void Program.F4(ref R r) - [ScopedRef] ref R r -void Program.F5(in R r) - [ScopedRef] in R r"; +@"void Program.F4(scoped ref R r) + [ScopedRef] scoped ref R r +void Program.F5(scoped in R r) + [ScopedRef] scoped in R r +"; CompileAndVerify(comp, symbolValidator: module => { Assert.Equal("System.Runtime.CompilerServices.ScopedRefAttribute", GetScopedRefType(module).ToTestDisplayString()); @@ -436,16 +437,16 @@ public void EmitAttribute_DelegateParameters() "; var comp = CreateCompilation(source); var expected = -@"void D.Invoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, R y) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x - [ScopedRef] R y -System.IAsyncResult D.BeginInvoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, R y, System.AsyncCallback callback, System.Object @object) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x - [ScopedRef] R y +@"void D.Invoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, scoped R y) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x + [ScopedRef] scoped R y +System.IAsyncResult D.BeginInvoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, scoped R y, System.AsyncCallback callback, System.Object @object) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x + [ScopedRef] scoped R y System.AsyncCallback callback System.Object @object -void D.EndInvoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, System.IAsyncResult result) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x +void D.EndInvoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x, System.IAsyncResult result) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 x System.IAsyncResult result "; CompileAndVerify( @@ -473,17 +474,17 @@ static void Main() }"; var comp = CreateCompilation(source); var expected = -@"void D.Invoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i -System.IAsyncResult D.BeginInvoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i, System.AsyncCallback callback, System.Object @object) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i +@"void D.Invoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i +System.IAsyncResult D.BeginInvoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i, System.AsyncCallback callback, System.Object @object) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i System.AsyncCallback callback System.Object @object -void D.EndInvoke(in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i, System.IAsyncResult result) - [ScopedRef] in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i +void D.EndInvoke(scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i, System.IAsyncResult result) + [ScopedRef] scoped in modreq(System.Runtime.InteropServices.InAttribute) System.Int32 i System.IAsyncResult result -void Program.<>c.
b__0_0(in System.Int32 i) - [ScopedRef] in System.Int32 i +void Program.<>c.
b__0_0(scoped in System.Int32 i) + [ScopedRef] scoped in System.Int32 i "; CompileAndVerify( source, @@ -509,8 +510,8 @@ void L(scoped in int i) { } }"; var comp = CreateCompilation(source); var expected = -@"void Program.g__L|0_0(in System.Int32 i) - [ScopedRef] in System.Int32 i +@"void Program.g__L|0_0(scoped in System.Int32 i) + [ScopedRef] scoped in System.Int32 i "; CompileAndVerify( source, @@ -539,14 +540,14 @@ static void Main() }"; var comp = CreateCompilation(source); var expected = -@"void <>f__AnonymousDelegate0.Invoke(in System.Int32 value) - [ScopedRef] in System.Int32 value -R <>f__AnonymousDelegate1.Invoke(R value) - [ScopedRef] R value -void Program.<>c.
b__0_0(in System.Int32 i) - [ScopedRef] in System.Int32 i -R Program.<>c.
b__0_1(R r) - [ScopedRef] R r +@"void <>f__AnonymousDelegate0.Invoke(scoped in System.Int32 arg) + [ScopedRef] scoped in System.Int32 arg +R <>f__AnonymousDelegate1.Invoke(scoped R arg) + [ScopedRef] scoped R arg +void Program.<>c.
b__0_0(scoped in System.Int32 i) + [ScopedRef] scoped in System.Int32 i +R Program.<>c.
b__0_1(scoped R r) + [ScopedRef] scoped R r "; CompileAndVerify( source, diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs index 3cb0d18007996..9b2fbcdf6818c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_WellKnownAttributes.cs @@ -13803,5 +13803,67 @@ public struct S2 { } Diagnostic(ErrorCode.ERR_InvalidNamedArgument, "CharSet=0").WithArguments("CharSet").WithLocation(7, 38) ); } + + [Fact, WorkItem(64605, "https://github.com/dotnet/roslyn/issues/64605")] + public void ObsoleteWithInterpolation() + { + var source = """ +using System; + +internal class Program +{ + static void Main(string[] args) + { + var object1 = new LegacyObject1(); + var object2 = new LegacyObject2(); + } +} + +[Obsolete($"Do not use {nameof(LegacyObject1)}")] +public class LegacyObject1 +{ +} + +[Obsolete("Do not use" + nameof(LegacyObject2))] +public class LegacyObject2 +{ +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,27): warning CS0618: 'LegacyObject1' is obsolete: 'Do not use LegacyObject1' + // var object1 = new LegacyObject1(); + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "LegacyObject1").WithArguments("LegacyObject1", "Do not use LegacyObject1").WithLocation(7, 27), + // (8,27): warning CS0618: 'LegacyObject2' is obsolete: 'Do not useLegacyObject2' + // var object2 = new LegacyObject2(); + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "LegacyObject2").WithArguments("LegacyObject2", "Do not useLegacyObject2").WithLocation(8, 27) + ); + } + + [Fact, WorkItem(64605, "https://github.com/dotnet/roslyn/issues/64605")] + public void ObsoleteWithInterpolationWithNonConstant() + { + var source = """ +using System; + +internal class Program +{ + static void Main(string[] args) + { + var object1 = new LegacyObject(); + } +} + +[Obsolete($"Do not use {nameof(LegacyObject)}{string.Empty}")] +public class LegacyObject +{ +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (11,11): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [Obsolete($"Do not use {nameof(LegacyObject)}{string.Empty}")] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, @"$""Do not use {nameof(LegacyObject)}{string.Empty}""").WithLocation(11, 11)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs index 07964c595f4a1..44f2b4be31b70 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.cs @@ -373,7 +373,7 @@ public void TestGetEffectiveDiagnostics() break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1057,7 +1057,7 @@ public void TestReportingDiagnosticWithInvalidLocation(AnalyzerWithInvalidDiagno break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } IFormattable context = $@"{string.Format(CodeAnalysisResources.ExceptionContext, contextDetail)} @@ -3811,7 +3811,7 @@ void verifyDiagnostics(ImmutableArray diagnostics) Assert.Equal(analyzer.Descriptor.Id, diagnostic.Id); Assert.Equal(LocationKind.ExternalFile, diagnostic.Location.Kind); var location = (ExternalFileLocation)diagnostic.Location; - Assert.Equal(additionalFile.Path, location.FilePath); + Assert.Equal(additionalFile.Path, location.GetLineSpan().Path); Assert.Equal(diagnosticSpan, location.SourceSpan); } } diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs index 9460fa67eaeb2..b2808db170bf5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs @@ -563,7 +563,7 @@ public void TestProgrammaticSuppressionInfo_DiagnosticSuppressor() Assert.Single(diagnostics); var programmaticSuppression = diagnostics.Select(d => d.ProgrammaticSuppressionInfo).Single(); Assert.Equal(2, programmaticSuppression.Suppressions.Count); - var orderedSuppressions = programmaticSuppression.Suppressions.Order().ToImmutableArrayOrEmpty(); + var orderedSuppressions = Roslyn.Utilities.EnumerableExtensions.Order(programmaticSuppression.Suppressions).ToImmutableArrayOrEmpty(); Assert.Equal(suppressionId, orderedSuppressions[0].Id); Assert.Equal(suppressor.SuppressionDescriptor.Justification, orderedSuppressions[0].Justification); Assert.Equal(suppressionId2, orderedSuppressions[1].Id); diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs index 156f71bfee9a2..a5d8567a1ef8c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs @@ -22,12 +22,13 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class NumericIntPtrTests : CSharpTestBase { + private static string IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null; + internal static readonly ConversionKind[] Identity = new[] { ConversionKind.Identity }; internal static readonly ConversionKind[] NoConversion = new[] { ConversionKind.NoConversion }; internal static readonly ConversionKind[] Boxing = new[] { ConversionKind.Boxing }; @@ -929,19 +930,18 @@ class A4 : IA void IA.F1(System.IntPtr x, nuint y) { } }"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var ref1 = comp.ToMetadataReference(); var ref2 = comp.EmitToImageReference(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref1, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref1 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref2, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref2 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -992,19 +992,18 @@ class B4 : A Diagnostic(ErrorCode.WRN_NewNotRequired, "F2").WithArguments("B3.F2(nint)").WithLocation(14, 21) }; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(diagnostics); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var ref1 = comp.ToMetadataReference(); var ref2 = comp.EmitToImageReference(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref1, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref1 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(diagnostics); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref2, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref2 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(diagnostics); } @@ -1019,11 +1018,146 @@ static partial void F2(System.UIntPtr x) { } static partial void F1(nint x) { } static partial void F2(nuint x); }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseDll.WithWarningLevel(5), parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningLevel(5), parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningLevel(6), parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void AddUIntPtrAndInt() + { + var source = @" +using System; + +class C +{ + UIntPtr M(UIntPtr i, int j) => i + j; +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (6,36): error CS0034: Operator '+' is ambiguous on operands of type 'nuint' and 'int' + // UIntPtr M(UIntPtr i, int j) => i + j; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "i + j").WithArguments("+", "nuint", "int").WithLocation(6, 36) + ); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp); + verifier.VerifyIL("C.M", @" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: call ""System.UIntPtr System.UIntPtr.op_Addition(System.UIntPtr, int)"" + IL_0007: ret +} +"); + } + + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void AddNUIntAndInt() + { + var source = @" +class C +{ + nuint M(nuint i, int j) => i + j; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (4,32): error CS0034: Operator '+' is ambiguous on operands of type 'nuint' and 'int' + // nuint M(nuint i, int j) => i + j; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "i + j").WithArguments("+", "nuint", "int").WithLocation(4, 32) + ); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,32): error CS0034: Operator '+' is ambiguous on operands of type 'nuint' and 'int' + // nuint M(nuint i, int j) => i + j; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "i + j").WithArguments("+", "nuint", "int").WithLocation(4, 32) + ); + } + + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void AddIntPtrAndInt() + { + var source = @" +using System; + +class C +{ + IntPtr M(IntPtr i, int j) => i + j; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + verifier.VerifyIL("C.M", @" +{ + // Code size 5 (0x5) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: conv.i + IL_0003: add + IL_0004: ret +} +"); + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + verifier = CompileAndVerify(comp); + verifier.VerifyIL("C.M", @" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: call ""System.IntPtr System.IntPtr.op_Addition(System.IntPtr, int)"" + IL_0007: ret +} +"); + } + + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void AddNIntAndInt() + { + var source = @" +class C +{ + nint M(nint i, int j) => i + j; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + verifier.VerifyIL("C.M", @" +{ + // Code size 5 (0x5) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: conv.i + IL_0003: add + IL_0004: ret +} +"); - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseDll.WithWarningLevel(6), parseOptions: TestOptions.Regular9); + comp = CreateCompilation(source); comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp); + verifier.VerifyIL("C.M", @" +{ + // Code size 5 (0x5) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: conv.i + IL_0003: add + IL_0004: ret +} +"); } [Fact] @@ -1051,19 +1185,18 @@ static void Main() } }"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var ref1 = comp.ToMetadataReference(); var ref2 = comp.EmitToImageReference(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref1, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref1 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref2, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { ref2 }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -1095,10 +1228,10 @@ class AAttribute : System.Attribute Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "nuint").WithArguments("nuint").WithLocation(2, 5) }; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -1117,7 +1250,7 @@ static void Main() }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (6,34): error CS0103: The name 'nint' does not exist in the current context // Console.WriteLine(nameof(nint)); @@ -1140,10 +1273,10 @@ static void F(int @nint, uint @nuint) } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -1161,7 +1294,7 @@ static void Main() _ = sizeof(nuint); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,13): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context @@ -1193,9 +1326,9 @@ unsafe static void Main() Console.Write(sizeof(nuint)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); int size = IntPtr.Size; - var verifier = CompileAndVerify(comp, expectedOutput: $"{size}{size}{size}{size}"); + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput($"{size}{size}{size}{size}"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.Main", @"{ // Code size 45 (0x2d) @@ -1227,7 +1360,7 @@ static IEnumerable F() yield return sizeof(System.UIntPtr); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (6,22): error CS1629: Unsafe code may not appear in iterators // yield return sizeof(nint); @@ -1263,13 +1396,13 @@ static void Main() WriteLine((object)t2 == t4); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); - CompileAndVerify(comp, expectedOutput: + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"System.IntPtr System.UIntPtr False True -True"); +True")); } [Fact] @@ -1287,8 +1420,8 @@ static void Main() System.Console.WriteLine(F()); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: @"1"); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(@"1"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.F", @"{ // Code size 16 (0x10) @@ -1322,10 +1455,10 @@ static void Main() System.Console.WriteLine(F2(42)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); - var verifier = CompileAndVerify(comp, expectedOutput: + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"-42 -42"); +42")); verifier.VerifyIL("Program.F1", @"{ // Code size 8 (0x8) @@ -1351,7 +1484,7 @@ .maxstack 1 [Fact] public void BuiltInOperators() { - var comp = CreateNumericIntPtrCompilation("", new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation("", parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); verifyOperators(comp); @@ -1456,8 +1589,7 @@ public class A public static {{nuintType}}? F4; } """; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var sourceB = @@ -1478,9 +1610,9 @@ static void M2(int x, uint y, int? z, uint? w) F4 = w; } }"; - comp = CreateEmptyCompilation(sourceB, references: new[] { AsReference(comp, useCompilationReference), mscorlibRefWithoutSharing }, parseOptions: useLatest ? TestOptions.Regular9 : TestOptions.Regular8); + comp = CreateCompilation(sourceB, references: new[] { AsReference(comp, useCompilationReference) }, parseOptions: useLatest ? TestOptions.Regular9 : TestOptions.Regular8, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); verifier.VerifyIL("B.M1", @"{ // Code size 59 (0x3b) @@ -1494,18 +1626,18 @@ .locals init (nint? V_0, IL_000c: ldsfld ""nint? A.F3"" IL_0011: stloc.0 IL_0012: ldloca.s V_0 - IL_0014: call ""bool nint?.HasValue.get"" + IL_0014: call ""readonly bool nint?.HasValue.get"" IL_0019: brfalse.s IL_0023 IL_001b: ldloca.s V_0 - IL_001d: call ""nint nint?.GetValueOrDefault()"" + IL_001d: call ""readonly nint nint?.GetValueOrDefault()"" IL_0022: pop IL_0023: ldsfld ""nuint? A.F4"" IL_0028: stloc.1 IL_0029: ldloca.s V_1 - IL_002b: call ""bool nuint?.HasValue.get"" + IL_002b: call ""readonly bool nuint?.HasValue.get"" IL_0030: brfalse.s IL_003a IL_0032: ldloca.s V_1 - IL_0034: call ""nuint nuint?.GetValueOrDefault()"" + IL_0034: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_0039: pop IL_003a: ret }"); @@ -1526,28 +1658,28 @@ .locals init (int? V_0, IL_000e: ldarg.2 IL_000f: stloc.0 IL_0010: ldloca.s V_0 - IL_0012: call ""bool int?.HasValue.get"" + IL_0012: call ""readonly bool int?.HasValue.get"" IL_0017: brtrue.s IL_0024 IL_0019: ldloca.s V_1 IL_001b: initobj ""nint?"" IL_0021: ldloc.1 IL_0022: br.s IL_0031 IL_0024: ldloca.s V_0 - IL_0026: call ""int int?.GetValueOrDefault()"" + IL_0026: call ""readonly int int?.GetValueOrDefault()"" IL_002b: conv.i IL_002c: newobj ""nint?..ctor(nint)"" IL_0031: stsfld ""nint? A.F3"" IL_0036: ldarg.3 IL_0037: stloc.2 IL_0038: ldloca.s V_2 - IL_003a: call ""bool uint?.HasValue.get"" + IL_003a: call ""readonly bool uint?.HasValue.get"" IL_003f: brtrue.s IL_004c IL_0041: ldloca.s V_3 IL_0043: initobj ""nuint?"" IL_0049: ldloc.3 IL_004a: br.s IL_0059 IL_004c: ldloca.s V_2 - IL_004e: call ""uint uint?.GetValueOrDefault()"" + IL_004e: call ""readonly uint uint?.GetValueOrDefault()"" IL_0053: conv.u IL_0054: newobj ""nuint?..ctor(nuint)"" IL_0059: stsfld ""nuint? A.F4"" @@ -1556,7 +1688,7 @@ .locals init (int? V_0, } [Theory, CombinatorialData] - public void BuiltInOperators_NativeIntegers(bool useCompilationReference, bool useSystemTypes) + public void BuiltInOperators_NativeIntegers(bool useCSharp9, bool useCompilationReference, bool useSystemTypes) { var nintType = useSystemTypes ? "System.IntPtr" : "nint"; var nuintType = useSystemTypes ? "System.UIntPtr" : "nuint"; @@ -1570,8 +1702,7 @@ public class A public static {{nuintType}}? F4; } """; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var refA = AsReference(comp, useCompilationReference); @@ -1590,9 +1721,9 @@ static void Main() F4 = F4 / F2; } }"; - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: useCSharp9 ? TestOptions.Regular9 : TestOptions.Regular8, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); verifier.VerifyIL("B.Main", @"{ // Code size 247 (0xf7) @@ -1611,28 +1742,28 @@ .locals init (nint? V_0, IL_0015: ldsfld ""nint? A.F3"" IL_001a: stloc.0 IL_001b: ldloca.s V_0 - IL_001d: call ""bool nint?.HasValue.get"" + IL_001d: call ""readonly bool nint?.HasValue.get"" IL_0022: brtrue.s IL_002f IL_0024: ldloca.s V_1 IL_0026: initobj ""nint?"" IL_002c: ldloc.1 IL_002d: br.s IL_003c IL_002f: ldloca.s V_0 - IL_0031: call ""nint nint?.GetValueOrDefault()"" + IL_0031: call ""readonly nint nint?.GetValueOrDefault()"" IL_0036: neg IL_0037: newobj ""nint?..ctor(nint)"" IL_003c: stsfld ""nint? A.F3"" IL_0041: ldsfld ""nuint? A.F4"" IL_0046: stloc.2 IL_0047: ldloca.s V_2 - IL_0049: call ""bool nuint?.HasValue.get"" + IL_0049: call ""readonly bool nuint?.HasValue.get"" IL_004e: brtrue.s IL_005b IL_0050: ldloca.s V_3 IL_0052: initobj ""nuint?"" IL_0058: ldloc.3 IL_0059: br.s IL_0067 IL_005b: ldloca.s V_2 - IL_005d: call ""nuint nuint?.GetValueOrDefault()"" + IL_005d: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_0062: newobj ""nuint?..ctor(nuint)"" IL_0067: stsfld ""nuint? A.F4"" IL_006c: ldsfld ""nint A.F1"" @@ -1648,14 +1779,14 @@ .locals init (nint? V_0, IL_0092: ldsfld ""nint A.F1"" IL_0097: stloc.s V_4 IL_0099: ldloca.s V_0 - IL_009b: call ""bool nint?.HasValue.get"" + IL_009b: call ""readonly bool nint?.HasValue.get"" IL_00a0: brtrue.s IL_00ad IL_00a2: ldloca.s V_1 IL_00a4: initobj ""nint?"" IL_00aa: ldloc.1 IL_00ab: br.s IL_00bc IL_00ad: ldloca.s V_0 - IL_00af: call ""nint nint?.GetValueOrDefault()"" + IL_00af: call ""readonly nint nint?.GetValueOrDefault()"" IL_00b4: ldloc.s V_4 IL_00b6: mul IL_00b7: newobj ""nint?..ctor(nint)"" @@ -1665,47 +1796,68 @@ .locals init (nint? V_0, IL_00c7: ldsfld ""nuint A.F2"" IL_00cc: stloc.s V_5 IL_00ce: ldloca.s V_2 - IL_00d0: call ""bool nuint?.HasValue.get"" + IL_00d0: call ""readonly bool nuint?.HasValue.get"" IL_00d5: brtrue.s IL_00e2 IL_00d7: ldloca.s V_3 IL_00d9: initobj ""nuint?"" IL_00df: ldloc.3 IL_00e0: br.s IL_00f1 IL_00e2: ldloca.s V_2 - IL_00e4: call ""nuint nuint?.GetValueOrDefault()"" + IL_00e4: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_00e9: ldloc.s V_5 IL_00eb: div.un IL_00ec: newobj ""nuint?..ctor(nuint)"" IL_00f1: stsfld ""nuint? A.F4"" IL_00f6: ret }"); + } - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics( - // (5,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F1 = -F1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "-F1").WithArguments("native-sized integers", "9.0").WithLocation(5, 14), - // (6,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F2 = +F2; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "+F2").WithArguments("native-sized integers", "9.0").WithLocation(6, 14), - // (7,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F3 = -F3; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "-F3").WithArguments("native-sized integers", "9.0").WithLocation(7, 14), - // (8,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F4 = +F4; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "+F4").WithArguments("native-sized integers", "9.0").WithLocation(8, 14), - // (9,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F1 = F1 * F1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "F1 * F1").WithArguments("native-sized integers", "9.0").WithLocation(9, 14), - // (10,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F2 = F2 / F2; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "F2 / F2").WithArguments("native-sized integers", "9.0").WithLocation(10, 14), - // (11,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F3 = F3 * F1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "F3 * F1").WithArguments("native-sized integers", "9.0").WithLocation(11, 14), - // (12,14): error CS8400: Feature 'native-sized integers' is not available in C# 8.0. Please use language version 9.0 or greater. - // F4 = F4 / F2; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "F4 / F2").WithArguments("native-sized integers", "9.0").WithLocation(12, 14)); + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void IntPtrOperator_OnPlatformWithNumericIntPtr() + { + var source = """ +using System; + +class C +{ + public static void M() + { + IntPtr a = default; + bool b = a == IntPtr.Zero; + a += 1; + _ = a + 1; + } +} +"""; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7_3, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(63860, "https://github.com/dotnet/roslyn/issues/63860")] + public void IntPtrOperator_OnPlatformWithoutNumericIntPtr() + { + var source = """ +using System; + +class C +{ + public static void M() + { + IntPtr a = default; + bool b = a == IntPtr.Zero; + a += 1; + _ = a + 1; + } +} +"""; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); } [Theory] @@ -1725,7 +1877,7 @@ static void F(nint{typeQualifier} x, nuint{typeQualifier} y) _ = ~y; }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); @@ -1777,7 +1929,7 @@ static void F({type}{typeQualifier} x, {type}{typeQualifier} y) _ = x >> 1; }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); @@ -1860,7 +2012,7 @@ static void F2() }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (15,17): error CS0266: Cannot implicitly convert type 'uint' to 'nint'. An explicit conversion exists (are you missing a cast?) @@ -1962,7 +2114,7 @@ static void F2() }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,24): error CS0266: Cannot implicitly convert type 'nint' to 'sbyte'. An explicit conversion exists (are you missing a cast?) @@ -2035,7 +2187,7 @@ static void Main() Console.WriteLine(y); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (7,26): error CS0133: The expression being assigned to 'y' must be constant @@ -2063,12 +2215,12 @@ static void Main() } } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,30): warning CS8778: Constant value '1152921504606846975' may overflow 'nint' at runtime (use 'unchecked' syntax to override) // nint y = checked((nint)x); Diagnostic(ErrorCode.WRN_ConstOutOfRangeChecked, "(nint)x").WithArguments("1152921504606846975", "nint").WithLocation(9, 30)); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "System.OverflowException" : "1152921504606846975"); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "System.OverflowException" : "1152921504606846975"), verify: Verification.FailsPEVerify); } [WorkItem(45531, "https://github.com/dotnet/roslyn/issues/45531")] @@ -2086,7 +2238,7 @@ static void Main() Console.WriteLine(y); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (7,26): error CS0133: The expression being assigned to 'y' must be constant @@ -2104,9 +2256,9 @@ static void Main() Console.WriteLine(y); } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "-1" : "1152921504606846975"); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "-1" : "1152921504606846975")); } [Fact] @@ -2219,12 +2371,12 @@ static void Main() Console.Write(y); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedError is null ? Array.Empty() : new[] { expectedError }); if (expectedError == null || ErrorFacts.IsWarning((ErrorCode)expectedError.Code)) { - CompileAndVerify(comp, expectedOutput: expectedOutput); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedOutput)); } } } @@ -2269,7 +2421,7 @@ static void F({type} n) System.Console.WriteLine(n); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); string expectedOutput = @"0 -2147483648 @@ -2295,7 +2447,7 @@ static void F({type} n) 65535 65536 2147483647"; - var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); string expectedIL = @"{ // Code size 209 (0xd1) @@ -2410,7 +2562,7 @@ static void F({type} n) System.Console.WriteLine(n); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); string expectedOutput = @"0 0 @@ -2429,7 +2581,7 @@ static void F({type} n) 2147483647 2147483648 4294967295"; - var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); string expectedIL = @"{ // Code size 141 (0x8d) @@ -2523,7 +2675,7 @@ static void F(T n) { } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,19): error CS1503: Argument 1: cannot convert from 'sbyte' to 'ushort' // F(sbyte.MaxValue); // 1 @@ -2588,7 +2740,7 @@ static void Main() const nuint d = default; } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -2608,10 +2760,10 @@ public static void Main() System.Console.Write($""{A}, {B}, {C}, {D}""); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "0, 0, 0, 0"); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("0, 0, 0, 0")); } [Fact] @@ -2623,8 +2775,7 @@ public void Constants_Fields_02() public const nint C1 = -42; public const nuint C2 = 42; }"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(source0, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source0, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); var ref0 = comp.EmitToImageReference(); var source1 = @"using System; @@ -2636,10 +2787,10 @@ static void Main() Console.WriteLine(A.C2); } }"; - comp = CreateEmptyCompilation(source1, references: new[] { ref0, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: + comp = CreateCompilation(source1, references: new[] { ref0 }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"-42 -42"); +42")); } [Fact] @@ -2653,8 +2804,7 @@ public void Constants_ParameterDefaults() public static System.UIntPtr F3(System.UIntPtr u = 42) => u; public static nuint F4(nuint u = 42) => u; }"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(source0, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source0, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); var ref0 = comp.EmitToImageReference(); var source1 = @@ -2669,13 +2819,13 @@ static void Main() Console.WriteLine(A.F4()); } }"; - comp = CreateEmptyCompilation(source1, references: new[] { ref0, mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); + comp = CreateCompilation(source1, references: new[] { ref0 }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); - CompileAndVerify(comp, expectedOutput: + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"-42 -42 42 -42"); +42")); } [Fact] @@ -2691,7 +2841,7 @@ class Program const UIntPtr D = 0; const UIntPtr E = uint.MaxValue; }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); verify((FieldSymbol)comp.GetMember("Program.A"), int.MinValue, signed: true, negative: true); @@ -2733,7 +2883,7 @@ public A(object value) { } class B { }"; - var comp = CreateNumericIntPtrCompilation(source0, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source0, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,4): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type @@ -2766,7 +2916,7 @@ public A(IntPtr value) { } class B { }"; - var comp = CreateNumericIntPtrCompilation(source0, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source0, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,2): error CS0181: Attribute constructor parameter 'value' has type 'nint', which is not a valid attribute parameter type @@ -2866,25 +3016,24 @@ static void Main() -3 4"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); - CompileAndVerify(comp, expectedOutput: expectedOutput); + var comp = CreateCompilation(new[] { sourceA, sourceB }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); var ref1 = comp.ToMetadataReference(); var ref2 = comp.EmitToImageReference(); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref1, mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); - CompileAndVerify(comp, expectedOutput: expectedOutput); + comp = CreateCompilation(sourceB, references: new[] { ref1 }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref2, mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); - CompileAndVerify(comp, expectedOutput: expectedOutput); + comp = CreateCompilation(sourceB, references: new[] { ref2 }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref1, mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, expectedOutput: expectedOutput); + comp = CreateCompilation(sourceB, references: new[] { ref1 }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular8, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); - comp = CreateEmptyCompilation(sourceB, references: new[] { ref2, mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular8); - CompileAndVerify(comp, expectedOutput: expectedOutput); + comp = CreateCompilation(sourceB, references: new[] { ref2 }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular8, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expectedOutput), verify: Verification.FailsPEVerify); } [Fact] @@ -2940,16 +3089,16 @@ static void Main() Console.WriteLine(M(6)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"case 6: 5 case 2: 4 case 0xff: 3 case 0: 2 case 9999: 1 default: 0 -0"); +0")); verifier.VerifyIL("Program.M", @" { // Code size 201 (0xc9) @@ -3108,16 +3257,16 @@ static void Main() Console.WriteLine(M(6)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"case 6: 5 case 2: 4 case 0xff: 3 case 0: 2 case 9999: 1 default: 0 -0"); +0")); verifier.VerifyIL("Program.M", @" { // Code size 184 (0xb8) @@ -3249,7 +3398,7 @@ static string convFromNullableT(string conversion, string sourceType) => // Code size 9 (0x9) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""{sourceType} {sourceType}?.Value.get"" + IL_0002: call ""readonly {sourceType} {sourceType}?.Value.get"" IL_0007: {conversion} IL_0008: ret }}"; @@ -3258,7 +3407,7 @@ static string convRUnFromNullableT(string conversion, string sourceType) => // Code size 10 (0xa) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""{sourceType} {sourceType}?.Value.get"" + IL_0002: call ""readonly {sourceType} {sourceType}?.Value.get"" IL_0007: conv.r.un IL_0008: {conversion} IL_0009: ret @@ -3291,14 +3440,14 @@ .locals init ({sourceType}? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {sourceType}?.HasValue.get"" + IL_0004: call ""readonly bool {sourceType}?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""{destType}?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""{sourceType} {sourceType}?.GetValueOrDefault()"" + IL_0017: call ""readonly {sourceType} {sourceType}?.GetValueOrDefault()"" IL_001c: {conversion} IL_001d: newobj ""{destType}?..ctor({destType})"" IL_0022: ret @@ -3312,14 +3461,14 @@ .locals init ({sourceType}? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {sourceType}?.HasValue.get"" + IL_0004: call ""readonly bool {sourceType}?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""{destType}?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""{sourceType} {sourceType}?.GetValueOrDefault()"" + IL_0017: call ""readonly {sourceType} {sourceType}?.GetValueOrDefault()"" IL_001c: conv.r.un IL_001d: {conversion} IL_001e: newobj ""{destType}?..ctor({destType})"" @@ -3333,13 +3482,13 @@ .locals init ({sourceType}? V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {sourceType}?.HasValue.get"" + IL_0004: call ""readonly bool {sourceType}?.HasValue.get"" IL_0009: brtrue.s IL_000e IL_000b: ldc.i4.0 IL_000c: conv.u IL_000d: ret IL_000e: ldloca.s V_0 - IL_0010: call ""{sourceType} {sourceType}?.GetValueOrDefault()"" + IL_0010: call ""readonly {sourceType} {sourceType}?.GetValueOrDefault()"" IL_0015: call ""{method}"" IL_001a: ret }}"; @@ -3449,7 +3598,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "nuint?", destType: "nint", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -3458,7 +3607,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -3470,7 +3619,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""long decimal.op_Explicit(decimal)"" IL_000c: conv.i IL_000d: ret @@ -3479,7 +3628,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""long decimal.op_Explicit(decimal)"" IL_000c: conv.ovf.i IL_000d: ret @@ -3489,7 +3638,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "System.UIntPtr?", destType: "nint", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -3498,7 +3647,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -3654,14 +3803,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -3678,14 +3827,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""long decimal.op_Explicit(decimal)"" IL_0021: conv.i IL_0022: newobj ""nint?..ctor(nint)"" @@ -3699,14 +3848,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""long decimal.op_Explicit(decimal)"" IL_0021: conv.ovf.i IL_0022: newobj ""nint?..ctor(nint)"" @@ -3723,14 +3872,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -3886,7 +4035,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: conv.i8 IL_0008: call ""decimal decimal.op_Implicit(long)"" IL_000d: ret @@ -3896,7 +4045,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "nint?", destType: "System.UIntPtr", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -3905,7 +4054,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret } ", @@ -3934,14 +4083,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: conv.i8 IL_001d: call ""decimal decimal.op_Implicit(long)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -3955,14 +4104,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: conv.i8 IL_001d: call ""decimal decimal.op_Implicit(long)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -3979,14 +4128,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -4058,7 +4207,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret } ", @@ -4068,7 +4217,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "float?", destType: "nuint", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: convFromNullableT("conv.u", "float"), expectedCheckedIL: convFromNullableT("conv.ovf.u", "float")); @@ -4078,7 +4227,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""ulong decimal.op_Explicit(decimal)"" IL_000c: conv.u IL_000d: ret @@ -4087,7 +4236,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""ulong decimal.op_Explicit(decimal)"" IL_000c: conv.ovf.u.un IL_000d: ret @@ -4097,7 +4246,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }", expectedCheckedIL: convFromNullableT("conv.ovf.u", "nint")); @@ -4106,7 +4255,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); @@ -4243,14 +4392,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -4268,14 +4417,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""ulong decimal.op_Explicit(decimal)"" IL_0021: conv.u IL_0022: newobj ""nuint?..ctor(nuint)"" @@ -4289,14 +4438,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""ulong decimal.op_Explicit(decimal)"" IL_0021: conv.ovf.u.un IL_0022: newobj ""nuint?..ctor(nuint)"" @@ -4312,14 +4461,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -4476,7 +4625,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: conv.u8 IL_0008: call ""decimal decimal.op_Implicit(ulong)"" IL_000d: ret @@ -4487,7 +4636,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -4497,7 +4646,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); @@ -4524,14 +4673,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: conv.u8 IL_001d: call ""decimal decimal.op_Implicit(ulong)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -4545,14 +4694,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: conv.u8 IL_001d: call ""decimal decimal.op_Implicit(ulong)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -4568,14 +4717,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -4735,7 +4884,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: conv.i8 IL_0008: call ""decimal decimal.op_Implicit(long)"" IL_000d: ret @@ -4745,7 +4894,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "System.IntPtr?", destType: "System.UIntPtr", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -4754,7 +4903,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret } ", @@ -4783,14 +4932,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: conv.i8 IL_001d: call ""decimal decimal.op_Implicit(long)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -4804,14 +4953,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: conv.i8 IL_001d: call ""decimal decimal.op_Implicit(long)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -4828,14 +4977,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -5032,7 +5181,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "nuint?", destType: "System.IntPtr", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -5041,7 +5190,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -5053,7 +5202,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""long decimal.op_Explicit(decimal)"" IL_000c: conv.i IL_000d: ret @@ -5062,7 +5211,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""long decimal.op_Explicit(decimal)"" IL_000c: conv.ovf.i IL_000d: ret @@ -5072,7 +5221,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "System.UIntPtr?", destType: "System.IntPtr", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: @@ -5081,7 +5230,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -5110,14 +5259,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -5134,14 +5283,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""long decimal.op_Explicit(decimal)"" IL_0021: conv.i IL_0022: newobj ""nint?..ctor(nint)"" @@ -5155,14 +5304,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""long decimal.op_Explicit(decimal)"" IL_0021: conv.ovf.i IL_0022: newobj ""nint?..ctor(nint)"" @@ -5179,14 +5328,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -5342,7 +5491,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: conv.u8 IL_0008: call ""decimal decimal.op_Implicit(ulong)"" IL_000d: ret @@ -5353,7 +5502,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret } ", @@ -5363,7 +5512,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); @@ -5390,14 +5539,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: conv.u8 IL_001d: call ""decimal decimal.op_Implicit(ulong)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -5411,14 +5560,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""decimal?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: conv.u8 IL_001d: call ""decimal decimal.op_Implicit(ulong)"" IL_0022: newobj ""decimal?..ctor(decimal)"" @@ -5434,14 +5583,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret } @@ -5625,7 +5774,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret } ", @@ -5635,7 +5784,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); conversions(sourceType: "float?", destType: "System.UIntPtr", ExplicitNullableNumeric, expectedImplicitIL: null, expectedExplicitIL: convFromNullableT("conv.u", "float"), expectedCheckedIL: convFromNullableT("conv.ovf.u", "float")); @@ -5645,7 +5794,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""ulong decimal.op_Explicit(decimal)"" IL_000c: conv.u IL_000d: ret @@ -5654,7 +5803,7 @@ .maxstack 1 // Code size 14 (0xe) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""decimal decimal?.Value.get"" + IL_0002: call ""readonly decimal decimal?.Value.get"" IL_0007: call ""ulong decimal.op_Explicit(decimal)"" IL_000c: conv.ovf.u.un IL_000d: ret @@ -5665,7 +5814,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nint nint?.Value.get"" + IL_0002: call ""readonly nint nint?.Value.get"" IL_0007: ret } ", @@ -5675,7 +5824,7 @@ .maxstack 1 // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 - IL_0002: call ""nuint nuint?.Value.get"" + IL_0002: call ""readonly nuint nuint?.Value.get"" IL_0007: ret }"); @@ -5701,14 +5850,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -5726,14 +5875,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""ulong decimal.op_Explicit(decimal)"" IL_0021: conv.u IL_0022: newobj ""nuint?..ctor(nuint)"" @@ -5747,14 +5896,14 @@ .locals init (decimal? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool decimal?.HasValue.get"" + IL_0004: call ""readonly bool decimal?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""decimal decimal?.GetValueOrDefault()"" + IL_0017: call ""readonly decimal decimal?.GetValueOrDefault()"" IL_001c: call ""ulong decimal.op_Explicit(decimal)"" IL_0021: conv.ovf.u.un IL_0022: newobj ""nuint?..ctor(nuint)"" @@ -5770,14 +5919,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret } @@ -5818,7 +5967,7 @@ void convert(string sourceType, }} enum E {{ }} "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); @@ -5837,7 +5986,7 @@ enum E {{ }} if (expectedIL != null) { - var verifier = CompileAndVerify(comp, verify: useUnsafeContext ? Verification.Skipped : Verification.Passes); + var verifier = CompileAndVerify(comp, verify: useUnsafeContext ? Verification.Skipped : Verification.FailsPEVerify); verifier.VerifyIL("Program.Convert", expectedIL); } @@ -5933,14 +6082,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: newobj ""nint?..ctor(nint)"" IL_0021: ret }"); @@ -5953,14 +6102,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: newobj ""nuint?..ctor(nuint)"" IL_0021: ret }"); @@ -5975,14 +6124,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: neg IL_001d: newobj ""nint?..ctor(nint)"" IL_0022: ret @@ -6007,14 +6156,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nint nint?.GetValueOrDefault()"" + IL_0017: call ""readonly nint nint?.GetValueOrDefault()"" IL_001c: not IL_001d: newobj ""nint?..ctor(nint)"" IL_0022: ret @@ -6028,14 +6177,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""nuint nuint?.GetValueOrDefault()"" + IL_0017: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001c: not IL_001d: newobj ""nuint?..ctor(nuint)"" IL_0022: ret @@ -6055,7 +6204,7 @@ static void Main() System.Console.WriteLine(Evaluate({operand})); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees[0]; @@ -6066,7 +6215,7 @@ static void Main() if (expectedDiagnostics.Length == 0) { - var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedResult)); verifier.VerifyIL("Program.Evaluate", expectedIL); } } @@ -6121,14 +6270,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_0018: call ""readonly nint nint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: add IL_001f: newobj ""nint?..ctor(nint)"" @@ -6157,14 +6306,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_0018: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: add IL_001f: newobj ""nuint?..ctor(nuint)"" @@ -6193,14 +6342,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_0018: call ""readonly nint nint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: sub IL_001f: newobj ""nint?..ctor(nint)"" @@ -6229,14 +6378,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_0018: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: sub IL_001f: newobj ""nuint?..ctor(nuint)"" @@ -6266,14 +6415,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_0018: call ""readonly nint nint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: add.ovf IL_001f: newobj ""nint?..ctor(nint)"" @@ -6302,14 +6451,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_0018: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: add.ovf.un IL_001f: newobj ""nuint?..ctor(nuint)"" @@ -6338,14 +6487,14 @@ .locals init (nint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nint?.HasValue.get"" + IL_0004: call ""readonly bool nint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nint nint?.GetValueOrDefault()"" + IL_0018: call ""readonly nint nint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: sub.ovf IL_001f: newobj ""nint?..ctor(nint)"" @@ -6374,14 +6523,14 @@ .locals init (nuint? V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool nuint?.HasValue.get"" + IL_0004: call ""readonly bool nuint?.HasValue.get"" IL_0009: brtrue.s IL_0016 IL_000b: ldloca.s V_1 IL_000d: initobj ""nuint?"" IL_0013: ldloc.1 IL_0014: br.s IL_0024 IL_0016: ldloca.s V_0 - IL_0018: call ""nuint nuint?.GetValueOrDefault()"" + IL_0018: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_001d: ldc.i4.1 IL_001e: sub.ovf.un IL_001f: newobj ""nuint?..ctor(nuint)"" @@ -6428,7 +6577,7 @@ static void Main() }} }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees[0]; @@ -6442,7 +6591,7 @@ static void Main() if (expectedDiagnostics.Length == 0) { - var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedResult)); verifier.VerifyIL("Program.Evaluate", expectedIL); } } @@ -6498,14 +6647,14 @@ .locals init (nint? V_0, IL_0002: ldobj ""nint?"" IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: call ""bool nint?.HasValue.get"" + IL_000a: call ""readonly bool nint?.HasValue.get"" IL_000f: brtrue.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj ""nint?"" IL_0019: ldloc.1 IL_001a: br.s IL_002a IL_001c: ldloca.s V_0 - IL_001e: call ""nint nint?.GetValueOrDefault()"" + IL_001e: call ""readonly nint nint?.GetValueOrDefault()"" IL_0023: ldc.i4.1 IL_0024: add IL_0025: newobj ""nint?..ctor(nint)"" @@ -6536,14 +6685,14 @@ .locals init (nuint? V_0, IL_0002: ldobj ""nuint?"" IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: call ""bool nuint?.HasValue.get"" + IL_000a: call ""readonly bool nuint?.HasValue.get"" IL_000f: brtrue.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj ""nuint?"" IL_0019: ldloc.1 IL_001a: br.s IL_002a IL_001c: ldloca.s V_0 - IL_001e: call ""nuint nuint?.GetValueOrDefault()"" + IL_001e: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_0023: ldc.i4.1 IL_0024: add IL_0025: newobj ""nuint?..ctor(nuint)"" @@ -6574,14 +6723,14 @@ .locals init (nint? V_0, IL_0002: ldobj ""nint?"" IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: call ""bool nint?.HasValue.get"" + IL_000a: call ""readonly bool nint?.HasValue.get"" IL_000f: brtrue.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj ""nint?"" IL_0019: ldloc.1 IL_001a: br.s IL_002a IL_001c: ldloca.s V_0 - IL_001e: call ""nint nint?.GetValueOrDefault()"" + IL_001e: call ""readonly nint nint?.GetValueOrDefault()"" IL_0023: ldc.i4.1 IL_0024: sub IL_0025: newobj ""nint?..ctor(nint)"" @@ -6612,14 +6761,14 @@ .locals init (nuint? V_0, IL_0002: ldobj ""nuint?"" IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: call ""bool nuint?.HasValue.get"" + IL_000a: call ""readonly bool nuint?.HasValue.get"" IL_000f: brtrue.s IL_001c IL_0011: ldloca.s V_1 IL_0013: initobj ""nuint?"" IL_0019: ldloc.1 IL_001a: br.s IL_002a IL_001c: ldloca.s V_0 - IL_001e: call ""nuint nuint?.GetValueOrDefault()"" + IL_001e: call ""readonly nuint nuint?.GetValueOrDefault()"" IL_0023: ldc.i4.1 IL_0024: sub IL_0025: newobj ""nuint?..ctor(nuint)"" @@ -6662,7 +6811,7 @@ static void Main() }} }} }}"; - var comp = CreateNumericIntPtrCompilation(source, new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees[0]; @@ -6674,7 +6823,7 @@ static void Main() if (expectedDiagnostics.Length == 0) { - var verifier = CompileAndVerify(comp, expectedOutput: expectedResult); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedResult)); verifier.VerifyIL("Program.Evaluate", expectedIL); } } @@ -6835,7 +6984,7 @@ static void F(MyInt x, MyInt y) _ = x << 1; } }"; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expected); } } @@ -6853,7 +7002,7 @@ void unifiedBinaryOps(string op, string leftType, string rightType, string expec "nint?" => "System.IntPtr?", "nuint" => "System.UIntPtr", "nuint?" => "System.UIntPtr?", - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; binaryOps(op, fullLeftType, rightType, expectedSymbol1, expectedSymbol2, diagnostics1, diagnostics2); } @@ -7936,7 +8085,7 @@ void binaryOperator(string op, string leftType, string rightType, string expecte return x {op} y; }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithAllowUnsafe(useUnsafeContext), parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees[0]; @@ -7947,7 +8096,7 @@ void binaryOperator(string op, string leftType, string rightType, string expecte if (expectedDiagnostics.Length == 0) { - CompileAndVerify(comp); + CompileAndVerify(comp, verify: Verification.FailsPEVerify); } static bool useUnsafe(string type) => type == "void*"; @@ -7997,9 +8146,9 @@ static void Main() Console.WriteLine(ShiftRight(35, 4)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"7 -1 12 @@ -8015,7 +8164,7 @@ static void Main() 7 6 560 -2"); +2")); verifier.VerifyIL("Program.Add", @"{ // Code size 4 (0x4) @@ -8223,9 +8372,9 @@ static void Main() Console.WriteLine(ShiftRight(35, 4)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"7 1 12 @@ -8241,7 +8390,7 @@ static void Main() 7 6 560 -2"); +2")); verifier.VerifyIL("Program.Add", @"{ // Code size 4 (0x4) @@ -8427,14 +8576,14 @@ static void Main() Console.WriteLine(Mod(5, 2)); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"7 -1 12 2 -1"); +1")); verifier.VerifyIL("Program.Add", @"{ // Code size 4 (0x4) @@ -8503,14 +8652,14 @@ static void Main() Console.WriteLine(Mod(5, 2)); } }"; - var comp = CreateNumericIntPtrCompilation(source, new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"7 1 12 2 -1"); +1")); verifier.VerifyIL("Program.Add", @"{ // Code size 4 (0x4) @@ -8828,8 +8977,7 @@ void constantDeclaration(string opType, string declarations, string expr, string {declarations} public const {opType} F = {expr}; }}"; - var mscorlibRefWithoutSharing = MscorlibRefWithoutSharingCachedSymbols; - var comp = CreateNumericIntPtrCompilation(sourceA, references: new[] { mscorlibRefWithoutSharing }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(expectedDiagnostics); @@ -8848,9 +8996,10 @@ static void Main() } }"; var refA = comp.EmitToImageReference(); - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithoutSharing }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + comp = CreateCompilation(sourceB, references: new[] { refA }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - CompileAndVerify(comp, expectedOutput: expectedResult); + // Investigating flaky IL verification issue. Tracked by https://github.com/dotnet/roslyn/issues/63782 + CompileAndVerify(comp, verify: Verification.PassesOrFailFast | Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedResult)); Assert.NotNull(expectedResult); } @@ -8875,7 +9024,7 @@ static void Main() Console.WriteLine(result); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); if (expectedDiagnostics.Any(d => ErrorFacts.GetSeverity((ErrorCode)d.Code) == DiagnosticSeverity.Error)) { @@ -8884,7 +9033,8 @@ static void Main() return; } - CompileAndVerify(comp, expectedOutput: expectedResult).VerifyDiagnostics(expectedDiagnostics); + // Investigating flaky IL verification issue. Tracked by https://github.com/dotnet/roslyn/issues/63782 + CompileAndVerify(comp, verify: Verification.FailsPEVerify | Verification.PassesOrFailFast, expectedOutput: IncludeExpectedOutput(expectedResult)).VerifyDiagnostics(expectedDiagnostics); Assert.NotNull(expectedResult); } } @@ -8903,7 +9053,7 @@ static void Main() const UIntPtr y = checked(uint.MaxValue + (UIntPtr)42); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,27): error CS0133: The expression being assigned to 'x' must be constant @@ -8935,8 +9085,8 @@ static void Main() } } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "System.OverflowException" : "4294967337").VerifyDiagnostics( + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "System.OverflowException" : "4294967337")).VerifyDiagnostics( // (10,29): warning CS8973: The operation may overflow 'nuint' at runtime (use 'unchecked' syntax to override) // var y = checked(uint.MaxValue + (UIntPtr)42); Diagnostic(ErrorCode.WRN_CompileTimeCheckedOverflow, "uint.MaxValue + (UIntPtr)42").WithArguments("nuint").WithLocation(10, 29) @@ -8953,8 +9103,8 @@ static void Main() System.Console.WriteLine(y); } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "41" : "4294967337").VerifyDiagnostics(); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "41" : "4294967337")).VerifyDiagnostics(); } [Fact] @@ -8971,7 +9121,7 @@ static void Main() const IntPtr y = checked(-(IntPtr)int.MinValue); } }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,26): error CS0133: The expression being assigned to 'x' must be constant // const IntPtr x = unchecked(-(IntPtr)int.MinValue); @@ -9002,8 +9152,8 @@ static void Main() } } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "System.OverflowException" : "2147483648").VerifyDiagnostics( + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "System.OverflowException" : "2147483648")).VerifyDiagnostics( // (10,29): warning CS8973: The operation may overflow 'nint' at runtime (use 'unchecked' syntax to override) // var y = checked(-(IntPtr)int.MinValue); Diagnostic(ErrorCode.WRN_CompileTimeCheckedOverflow, "-(IntPtr)int.MinValue").WithArguments("nint").WithLocation(10, 29) @@ -9020,8 +9170,8 @@ static void Main() System.Console.WriteLine(y); } }"; - comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: IntPtr.Size == 4 ? "-2147483648" : "2147483648").VerifyDiagnostics(); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(IntPtr.Size == 4 ? "-2147483648" : "2147483648")).VerifyDiagnostics(); } // OverflowException behavior is consistent with unchecked int division. @@ -9059,9 +9209,9 @@ static object Execute(Func f) static nint NativeIntDivision(IntPtr x, IntPtr y) => unchecked(x / y); static nint NativeIntRemainder(IntPtr x, IntPtr y) => unchecked(x % y); }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - CompileAndVerify(comp, expectedOutput: + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( $@"2147483647 System.OverflowException 0 @@ -9069,7 +9219,7 @@ static object Execute(Func f) 2147483647 {(IntPtr.Size == 4 ? "System.OverflowException" : "2147483648")} 0 -{(IntPtr.Size == 4 ? "System.OverflowException" : "0")}"); +{(IntPtr.Size == 4 ? "System.OverflowException" : "0")}")); } [Fact] @@ -9088,12 +9238,12 @@ static void Main() static nint LeftShift(nint x, int y) => unchecked(x << y); static void Report(long l) => Console.WriteLine(""{0:x}"", l); }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); var expectedValue = IntPtr.Size == 4 ? "fffffffffffffffe" : "fffffffe"; - CompileAndVerify(comp, expectedOutput: + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( $@"{expectedValue} -{expectedValue}"); +{expectedValue}")); } [Fact] @@ -9112,12 +9262,12 @@ static void Main() static nuint LeftShift(nuint x, int y) => unchecked(x << y); static void Report(ulong u) => Console.WriteLine(""{0:x}"", u); }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); var expectedValue = IntPtr.Size == 4 ? "fffffffe" : "1fffffffe"; - CompileAndVerify(comp, expectedOutput: + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( $@"{expectedValue} -{expectedValue}"); +{expectedValue}")); } [Fact] @@ -9141,7 +9291,7 @@ class C : I S I.F3() => default; S I.F4() => default; }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); @@ -9176,7 +9326,7 @@ class B : A public override nint[] F3() => null; public override System.IntPtr[] F4() => null; }"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var type = comp.GetTypeByMetadataName("A"); @@ -9241,11 +9391,11 @@ static void Main() Console.WriteLine(Execute(() => ConvertChecked(value))); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( $@"{toValueUnchecked} -{toValueChecked}"); +{toValueChecked}")); verifier.VerifyIL("Program.Convert", toConvUnchecked is null ? @" @@ -9322,7 +9472,7 @@ static void Main() Console.WriteLine(Execute(() => (ulong)ToPointer4(uint.MaxValue))); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); string expectedOutput = $@"-42 -42 @@ -9348,7 +9498,7 @@ static void Main() 42 System.OverflowException 4294967295"; - var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: expectedOutput); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(expectedOutput)); verifier.VerifyIL("Program.ToPointer1", @"{ // Code size 2 (0x2) @@ -9460,7 +9610,7 @@ static void F4() u = E.B; } }"; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (10,13): error CS0266: Cannot implicitly convert type 'nint' to 'E'. An explicit conversion exists (are you missing a cast?) // e = i1; @@ -9516,12 +9666,12 @@ static void Main() WriteLine(F4(E.B)); } }"; - var comp = CreateNumericIntPtrCompilation(new[] { sourceA, sourceB }, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: + var comp = CreateCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( @"A B -1 -1"); +1")); } [Fact] @@ -9646,11 +9796,11 @@ static void Main() Console.WriteLine(Execute(() => ConvertChecked(value))); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); - var verifier = CompileAndVerify(comp, expectedOutput: + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput( $@"{toValueUnchecked} -{toValueChecked}"); +{toValueChecked}")); verifier.VerifyIL("Program.Convert", $@"{{ @@ -9718,7 +9868,7 @@ static void F3(I<{nativeIntegerType}> x, I<{underlyingType}> y) ToPointer(); }} }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9, options: TestOptions.UnsafeReleaseDll); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -9731,7 +9881,7 @@ class C1 where U : I, I { } class C2 where U : I, I { } class C3 where U : I, I, I { } "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (2,35): error CS0405: Duplicate constraint 'I' for type parameter 'U' // class C1 where U : I, I { } @@ -9753,7 +9903,7 @@ class C1 : I, I { } class C2 : I, I { } class C3 : I, I, I { } "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (2,21): error CS0528: 'I' is already listed in interface list // class C1 : I, I { } @@ -9785,7 +9935,7 @@ public void SignedToUnsignedConversions_Implicit(string type) static {type} Checked4(long x) => checked(x); // 9 static {type} Checked5(nint x) => checked(x); // 10 }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,40): error CS0266: Cannot implicitly convert type 'sbyte' to 'nuint'. An explicit conversion exists (are you missing a cast?) // static nuint Implicit1(sbyte x) => x; // 1 @@ -9838,8 +9988,8 @@ public void SignedToUnsignedConversions_Explicit(string type) static {type} Checked4(long x) => checked(({type})x); static {type} Checked5(nint x) => checked(({type})x); }}"; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); - var verifier = CompileAndVerify(comp); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); string expectedExplicitILNop = @"{ // Code size 2 (0x2) @@ -10088,7 +10238,7 @@ unsafe class FinalType } enum E { } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Net70); if (noConversion) { @@ -10315,7 +10465,7 @@ class Indexers2 System.UIntPtr Property4 => throw null; } "; - var comp = CreateNumericIntPtrCompilation(new[] { source }, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(new[] { source }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); VerifyNoNativeIntegerAttributeEmitted(comp); @@ -10391,7 +10541,7 @@ class C2 { } class D { } "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -10402,7 +10552,7 @@ public void UnmanagedConstraint() class C where T : unmanaged { } class D : C { } "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -10524,9 +10674,9 @@ public void ArrayAccess() System.Console.Write($""{array[x1]}, {array[x2]}, {array[x3]}, {array[x4]}""); "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); - CompileAndVerify(comp, expectedOutput: "1, 2, 3, 4"); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("1, 2, 3, 4"), verify: Verification.FailsPEVerify); } [Fact] @@ -10537,12 +10687,12 @@ public class C { public nint M() { throw null; } }"; - var comp = CreateEmptyCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); var image = comp.EmitToImageReference(); - var comp2 = CreateNumericIntPtrCompilation(source, references: new[] { image, MscorlibRefWithoutSharingCachedSymbols }); + var comp2 = CreateCompilation(source, references: new[] { image }, targetFramework: TargetFramework.Net70); - CompileAndVerify(comp2, sourceSymbolValidator: verify, symbolValidator: verify); + CompileAndVerify(comp2, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.FailsPEVerify); static void verify(ModuleSymbol module) { @@ -10574,7 +10724,7 @@ public void PatternExplainer() }; "; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '> (nint)int.MaxValue' is not covered. // _ = x switch // 1 @@ -10642,13 +10792,13 @@ public static void M8(nuint{{s2Nullable}} x) { } public static void M9(byte{{s2Nullable}} x) { Write("M9(byte)"); } } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); // Note: conversions ushort->nint, ushort?->nint?, ushort->nint? are implicit (so rule 1 kicks in), but ushort?->nint is explicit (so rule 3 kicks in) var expected = (nullable1, nullable2) is (false, true) ? "M1 M2 M3 M4 M5(nint) M6 M7 M8 M9(nint)" : "M1 M2 M3 M4 M5(ushort) M6 M7 M8 M9(byte)"; - CompileAndVerify(comp, expectedOutput: expected); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expected), verify: Verification.FailsPEVerify); } [Theory, CombinatorialData] @@ -10710,13 +10860,13 @@ public static void M9(nuint{{s2Nullable}} x) { } public static void M10(byte{{s2Nullable}} x) { Write("M10(byte)"); } } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); // Note: conversions ushort->nint, ushort?->nint?, ushort->nint? are implicit (so rule 1 kicks in), but ushort?->nint is explicit (so rule 3 kicks in) var expected = (nullable1, nullable2) is (false, true) ? "M1 M2 M3 M4 M5 M6(IntPtr) M7 M8 M9 M10(IntPtr)" : "M1 M2 M3 M4 M5 M6(ushort) M7 M8 M9 M10(byte)"; - CompileAndVerify(comp, expectedOutput: expected); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput(expected), verify: Verification.FailsPEVerify); } [Fact] @@ -10737,17 +10887,8 @@ public class Derived : Base public override nint M() => 0; } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { libComp.ToMetadataReference(), MscorlibRefWithoutSharingCachedSymbols }); - comp.VerifyDiagnostics( - // warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1), - // warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1), - // warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1), - // warning CS1701: Assuming assembly reference 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' used by 'lib' matches identity 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' of 'mscorlib', you may need to supply runtime policy - Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin).WithArguments("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "lib", "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "mscorlib").WithLocation(1, 1) - ); + var comp = CreateCompilation(source, references: new[] { libComp.ToMetadataReference() }, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); var baseM = (RetargetingMethodSymbol)comp.GlobalNamespace.GetMember("Base.M"); var baseNint = (PENamedTypeSymbol)baseM.ReturnType; @@ -10768,7 +10909,7 @@ public class Base public virtual nint M() => 0; } """; - var libComp = CreateNumericIntPtrCompilation(lib_cs, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, assemblyName: "lib"); + var libComp = CreateCompilation(lib_cs, assemblyName: "lib", targetFramework: TargetFramework.Net70); libComp.VerifyDiagnostics(); string source = """ @@ -10778,7 +10919,11 @@ public class Derived : Base } """; var comp = CreateEmptyCompilation(source, references: new[] { libComp.ToMetadataReference(), MscorlibRef_v46 }); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (1,24): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. + // public class Derived : Base + Diagnostic(ErrorCode.ERR_NoTypeDef, "Base").WithArguments("System.Object", "System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a").WithLocation(1, 24) + ); var baseM = (RetargetingMethodSymbol)comp.GlobalNamespace.GetMember("Base.M"); var baseNint = (PENamedTypeSymbol)baseM.ReturnType; @@ -10806,9 +10951,9 @@ public class C System.UIntPtr M6(System.UIntPtr x, int count) => x >>> count; } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); verifier.VerifyIL("C.M1", shiftRight("nint")); verifier.VerifyIL("C.M2", shiftRight("nuint")); verifier.VerifyIL("C.M3", shiftRight("nint")); @@ -10868,13 +11013,13 @@ public unsafe static void Main() } } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "OVERFLOW RAN", verify: Verification.Skipped); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("OVERFLOW RAN"), verify: Verification.Skipped); comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "RAN", verify: Verification.Skipped); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("RAN"), verify: Verification.Skipped); } [Fact] @@ -10989,7 +11134,7 @@ public void XmlDoc_Cref(string type) /// Summary . class C { } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -11012,7 +11157,7 @@ public void XmlDoc_Cref_Alias() class C { } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -11040,7 +11185,7 @@ public class C } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -11065,7 +11210,7 @@ public class C } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -11090,7 +11235,7 @@ public class C } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (1,33): warning CS1574: XML comment has cref attribute 'type' that could not be resolved // /// Summary . @@ -11110,7 +11255,7 @@ public class C } """; - var comp = CreateNumericIntPtrCompilation(src, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, parseOptions: TestOptions.RegularWithDocumentationComments); + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularWithDocumentationComments, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -11229,7 +11374,7 @@ .locals init (nint V_0, IL_0002: ldarg.2 IL_0003: stloc.1 IL_0004: ldloca.s V_1 - IL_0006: call ""bool int?.HasValue.get"" + IL_0006: call ""readonly bool int?.HasValue.get"" IL_000b: brtrue.s IL_0017 IL_000d: ldloca.s V_2 IL_000f: initobj ""nint?"" @@ -11237,7 +11382,7 @@ .locals init (nint V_0, IL_0016: ret IL_0017: ldloc.0 IL_0018: ldloca.s V_1 - IL_001a: call ""int int?.GetValueOrDefault()"" + IL_001a: call ""readonly int int?.GetValueOrDefault()"" IL_001f: sizeof ""nint"" IL_0025: ldc.i4.8 IL_0026: mul @@ -11269,9 +11414,9 @@ .locals init (nint? V_0, IL_0002: ldarg.2 IL_0003: stloc.1 IL_0004: ldloca.s V_0 - IL_0006: call ""bool nint?.HasValue.get"" + IL_0006: call ""readonly bool nint?.HasValue.get"" IL_000b: ldloca.s V_1 - IL_000d: call ""bool int?.HasValue.get"" + IL_000d: call ""readonly bool int?.HasValue.get"" IL_0012: and IL_0013: brtrue.s IL_001f IL_0015: ldloca.s V_2 @@ -11279,9 +11424,9 @@ .locals init (nint? V_0, IL_001d: ldloc.2 IL_001e: ret IL_001f: ldloca.s V_0 - IL_0021: call ""nint nint?.GetValueOrDefault()"" + IL_0021: call ""readonly nint nint?.GetValueOrDefault()"" IL_0026: ldloca.s V_1 - IL_0028: call ""int int?.GetValueOrDefault()"" + IL_0028: call ""readonly int int?.GetValueOrDefault()"" IL_002d: sizeof ""nint"" IL_0033: ldc.i4.8 IL_0034: mul @@ -11375,14 +11520,14 @@ .locals init ({type} V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {type}.HasValue.get"" + IL_0004: call ""readonly bool {type}.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""{type}"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""{strippedType} {type}.GetValueOrDefault()"" + IL_0017: call ""readonly {strippedType} {type}.GetValueOrDefault()"" IL_001c: newobj ""{type}..ctor({strippedType})"" IL_0021: ret }}"; @@ -11399,14 +11544,14 @@ .locals init ({type} V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {type}.HasValue.get"" + IL_0004: call ""readonly bool {type}.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""{type}"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""{strippedType} {type}.GetValueOrDefault()"" + IL_0017: call ""readonly {strippedType} {type}.GetValueOrDefault()"" IL_001c: ldc.i4.1 IL_001d: {op} IL_001e: newobj ""{type}..ctor({strippedType})"" @@ -11426,14 +11571,14 @@ .locals init ({type} V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: call ""bool {type}.HasValue.get"" + IL_0004: call ""readonly bool {type}.HasValue.get"" IL_0009: brtrue.s IL_0015 IL_000b: ldloca.s V_1 IL_000d: initobj ""{type}"" IL_0013: ldloc.1 IL_0014: ret IL_0015: ldloca.s V_0 - IL_0017: call ""{strippedType} {type}.GetValueOrDefault()"" + IL_0017: call ""readonly {strippedType} {type}.GetValueOrDefault()"" IL_001c: ldc.i4.s {count} IL_001e: sizeof ""{strippedType}"" IL_0024: ldc.i4.8 @@ -11450,8 +11595,8 @@ .locals init ({type} V_0, CompilationVerifier compileAndVerify(CSharpTestSource source) { - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }); - return CompileAndVerify(comp); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + return CompileAndVerify(comp, verify: Verification.FailsPEVerify); } void validate(string type, string value, string binaryOp, string result32Bits, string result64Bits, string expectedIL) @@ -11508,8 +11653,8 @@ public static unsafe void Main() } } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: "RAN"); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net70); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("RAN")); verifier.VerifyIL("C.M", expectedIL); } } @@ -11525,9 +11670,9 @@ static class C public static nint ShiftRight(nint x) => x >> (-62); } """; - var comp = CreateNumericIntPtrCompilation(source, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "63"); + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("63"), verify: Verification.FailsPEVerify); verifier.VerifyIL("C.ShiftRight", @" { // Code size 16 (0x10) @@ -11569,13 +11714,13 @@ static void Main() var comp = CreateCompilation(new[] { SpanSource, source }, options: TestOptions.UnsafeReleaseExe); verify(comp); - comp = CreateNumericIntPtrCompilation(new[] { SpanSource, source }, references: new[] { MscorlibRefWithoutSharingCachedSymbols }, options: TestOptions.UnsafeReleaseExe); + comp = CreateCompilation(new[] { source }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net70); verify(comp); void verify(CSharpCompilation comp) { comp.VerifyEmitDiagnostics(); - var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "00"); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("00")); verifier.VerifyIL("Program.F", """ { // Code size 48 (0x30) @@ -11627,18 +11772,6 @@ private void VerifyNoNativeIntegerAttributeEmitted(CSharpCompilation comp) verify: Verification.Skipped); } - private static MetadataReference MscorlibRefWithoutSharingCachedSymbols - { - get - { - // Avoid sharing mscorlib symbols with other tests since we are about to change - // RuntimeSupportsNumericIntPtr property for it. - - return ((AssemblyMetadata)((MetadataImageReference)MscorlibRef).GetMetadata()).CopyWithoutSharingCachedSymbols(). - GetReference(display: "mscorlib.v4_0_30319.dll"); - } - } - const string RuntimeFeature_NumericIntPtr = @" namespace System.Runtime.CompilerServices { diff --git a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs index c8cb3243c71d9..4de723adb7056 100644 --- a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs @@ -5558,5 +5558,89 @@ public static void Test() Diagnostic(ErrorCode.ERR_UseDefViolation, "obj").WithArguments("obj").WithLocation(28, 17) ); } + + [Fact, WorkItem(63911, "https://github.com/dotnet/roslyn/issues/63911")] + public void LocalMethod_ParameterAttribute() + { + var source = """ + using System; + using System.Runtime.InteropServices; + class Program + { + static void Main() + { + const int N = 10; + void F([Optional, DefaultParameterValue(N)] int x) => Console.WriteLine(x); + F(); + } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem(63911, "https://github.com/dotnet/roslyn/issues/63911")] + public void Lambda_ParameterAttribute() + { + var source = """ + using System; + using System.Runtime.InteropServices; + class Program + { + static void Main() + { + const int N = 10; + var lam = ([Optional, DefaultParameterValue(N)] int x) => Console.WriteLine(x); + lam(100); + } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem(63911, "https://github.com/dotnet/roslyn/issues/63911")] + public void LocalMethod_ParameterAttribute_NamedArguments() + { + var source = """ + using System; + [AttributeUsage(AttributeTargets.Parameter)] + class A : Attribute + { + public int Prop { get; set; } + } + class Program + { + static void Main() + { + const int N = 10; + void F([A(Prop = N)] int x) { } + F(100); + } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem(63911, "https://github.com/dotnet/roslyn/issues/63911")] + public void Lambda_ParameterAttribute_NamedArguments() + { + var source = """ + using System; + [AttributeUsage(AttributeTargets.Parameter)] + class A : Attribute + { + public int Prop { get; set; } + } + class Program + { + static void Main() + { + const int N = 10; + var lam = ([A(Prop = N)] int x) => { }; + lam(100); + } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index 238c253b9cb1c..27f2ea81b0670 100644 --- a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.UnitTests - net6.0;net472 + net7.0;net472 true diff --git a/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs b/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs index dc62eb614b1e1..15446d280c917 100644 --- a/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs +++ b/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs @@ -101,13 +101,13 @@ bool runTestAndCatch(int depth) [ConditionalFact(typeof(WindowsOrLinuxOnly)), WorkItem(34880, "https://github.com/dotnet/roslyn/issues/34880")] public void OverflowOnFluentCall() { - int numberFluentCalls = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch + int numberFluentCalls = (IntPtr.Size, ExecutionConditionUtil.Configuration) switch { - (ExecutionArchitecture.x86, ExecutionConfiguration.Debug) => 520, // 510 - (ExecutionArchitecture.x86, ExecutionConfiguration.Release) => 1400, // 1310 - (ExecutionArchitecture.x64, ExecutionConfiguration.Debug) => 250, // 225, - (ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 700, // 620 - _ => throw new Exception($"Unexpected configuration {ExecutionConditionUtil.Architecture} {ExecutionConditionUtil.Configuration}") + (4, ExecutionConfiguration.Debug) => 520, // 510 + (4, ExecutionConfiguration.Release) => 1400, // 1310 + (8, ExecutionConfiguration.Debug) => 250, // 225, + (8, ExecutionConfiguration.Release) => 700, // 620 + _ => throw new Exception($"Unexpected configuration {IntPtr.Size * 8}-bit {ExecutionConditionUtil.Configuration}") }; // \xunit.console.exe "\CSharpCompilerEmitTest\Roslyn.Compilers.CSharp.Emit.UnitTests.dll" -noshadow -verbose -class "Microsoft.CodeAnalysis.CSharp.UnitTests.Emit.EndToEndTests" @@ -156,14 +156,14 @@ void M2() { [WorkItem(53361, "https://github.com/dotnet/roslyn/issues/53361")] public void DeeplyNestedGeneric() { - int nestingLevel = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch + int nestingLevel = (IntPtr.Size, ExecutionConditionUtil.Configuration) switch { // Legacy baselines are indicated by comments - (ExecutionArchitecture.x86, ExecutionConfiguration.Debug) => 370, // 270 - (ExecutionArchitecture.x86, ExecutionConfiguration.Release) => 1290, // 1290 - (ExecutionArchitecture.x64, ExecutionConfiguration.Debug) => 270, // 170 - (ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 730, // 730 - _ => throw new Exception($"Unexpected configuration {ExecutionConditionUtil.Architecture} {ExecutionConditionUtil.Configuration}") + (4, ExecutionConfiguration.Debug) => 370, // 270 + (4, ExecutionConfiguration.Release) => 1290, // 1290 + (8, ExecutionConfiguration.Debug) => 270, // 170 + (8, ExecutionConfiguration.Release) => 730, // 730 + _ => throw new Exception($"Unexpected configuration {IntPtr.Size * 8}-bit {ExecutionConditionUtil.Configuration}") }; // Un-comment loop below and use above commands to figure out the new limits @@ -231,13 +231,13 @@ public static void Main(string[] args) [ConditionalFact(typeof(WindowsOrLinuxOnly), typeof(NoIOperationValidation))] public void NestedIfStatements() { - int nestingLevel = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch + int nestingLevel = (IntPtr.Size, ExecutionConditionUtil.Configuration) switch { - (ExecutionArchitecture.x86, ExecutionConfiguration.Debug) => 310, - (ExecutionArchitecture.x86, ExecutionConfiguration.Release) => 1650, - (ExecutionArchitecture.x64, ExecutionConfiguration.Debug) => 200, - (ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 780, - _ => throw new Exception($"Unexpected configuration {ExecutionConditionUtil.Architecture} {ExecutionConditionUtil.Configuration}") + (4, ExecutionConfiguration.Debug) => 310, + (4, ExecutionConfiguration.Release) => 1650, + (8, ExecutionConfiguration.Debug) => 200, + (8, ExecutionConfiguration.Release) => 780, + _ => throw new Exception($"Unexpected configuration {IntPtr.Size * 8}-bit {ExecutionConditionUtil.Configuration}") }; RunTest(nestingLevel, runTest); @@ -277,13 +277,13 @@ static void Main() [ConditionalFact(typeof(WindowsOrLinuxOnly))] public void Constraints() { - int n = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch + int n = (IntPtr.Size, ExecutionConditionUtil.Configuration) switch { - (ExecutionArchitecture.x86, ExecutionConfiguration.Debug) => 420, - (ExecutionArchitecture.x86, ExecutionConfiguration.Release) => 1100, - (ExecutionArchitecture.x64, ExecutionConfiguration.Debug) => 180, - (ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 400, - _ => throw new Exception($"Unexpected configuration {ExecutionConditionUtil.Architecture} {ExecutionConditionUtil.Configuration}") + (4, ExecutionConfiguration.Debug) => 420, + (4, ExecutionConfiguration.Release) => 1100, + (8, ExecutionConfiguration.Debug) => 180, + (8, ExecutionConfiguration.Release) => 400, + _ => throw new Exception($"Unexpected configuration {IntPtr.Size * 8}-bit {ExecutionConditionUtil.Configuration}") }; RunTest(n, runTest); diff --git a/src/Compilers/CSharp/Test/EndToEnd/Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests.csproj b/src/Compilers/CSharp/Test/EndToEnd/Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests.csproj index d14796cc26fd9..e12828f7a832c 100644 --- a/src/Compilers/CSharp/Test/EndToEnd/Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/EndToEnd/Microsoft.CodeAnalysis.CSharp.EndToEnd.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.UnitTests - net6.0;net472 + net7.0;net472 true diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs index 1e264c3027a4d..1a324bc58ca58 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs @@ -3544,9 +3544,15 @@ public void DirectlyBindArgument_Attribute() [assembly: /**/System.CLSCompliant(isCompliant: true)/**/] "; string expectedOperationTree = @" -IOperation: (OperationKind.None, Type: System.CLSCompliantAttribute) (Syntax: 'System.CLSC ... iant: true)') - Children(1): - ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'System.CLSC ... iant: true)') + IObjectCreationOperation (Constructor: System.CLSCompliantAttribute..ctor(System.Boolean isCompliant)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: 'System.CLSC ... iant: true)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'isCompliant: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs new file mode 100644 index 0000000000000..d266ce865a973 --- /dev/null +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.cs @@ -0,0 +1,1302 @@ +// 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.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + [CompilerTrait(CompilerFeature.IOperation)] + public class IOperationTests_IAttributeOperation : SemanticModelTestBase + { + [Fact] + public void TestCallerInfoImplicitCall() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerLineNumber] int lineNumber = -1) + { + Console.WriteLine(lineNumber); + } +} + +[/**/My/**/] +class Test { } +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Int32 lineNumber = -1])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: lineNumber) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 13, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + string expectedFlowGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] +Block[B1] - Block + Predecessors: [B0] + Statements (1) + IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Int32 lineNumber = -1])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: lineNumber) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 13, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null + Next (Regular) Block[B2] +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Class() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string callerName = """") + { + Console.WriteLine(callerName); + } +} + +[/**/My/**/] +class Test +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String callerName = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: callerName) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: """", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Method() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string callerName = """") + { + Console.WriteLine(callerName); + } +} + +class Test +{ + [/**/My/**/] + public void M() { } +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String callerName = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: callerName) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestCallerMemberName_Parameter() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +public class C +{ + public void M([/**/My/**/] int x) + { + } +} + +internal class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string x = null) {} +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String x = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestNonExistingAttribute() + { + string source = @" +using System; + +[/**/My/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My') + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + Children(0) +"; + var expectedDiagnostics = new[] + { + // (4,12): error CS0246: The type or namespace name 'MyAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "My").WithArguments("MyAttribute").WithLocation(4, 12), + // (4,12): error CS0246: The type or namespace name 'My' could not be found (are you missing a using directive or an assembly reference?) + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "My").WithArguments("My").WithLocation(4, 12), + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithoutArguments() + { + string source = @" +using System; + +class MyAttribute : Attribute { } + +[/**/My/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor()) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(0) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgument() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value) { } +} + +[/**/My(""Value"")/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(""Value"")') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.String value)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(""Value"")') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '""Value""') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""Value"") (Syntax: '""Value""') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgument_IncorrectTypePassed() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value) { } +} + +[/**/My(0)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(0)') + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'My(0)') + Children(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') +"; + var expectedDiagnostics = new[] + { + // (9,15): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // [/**/My(0)/**/] + Diagnostic(ErrorCode.ERR_BadArgType, "0").WithArguments("1", "int", "string").WithLocation(9, 15) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithExplicitArgumentOptionalParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value = """") { } +} + +[/**/My(""Value"")/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(""Value"")') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String value = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(""Value"")') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '""Value""') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""Value"") (Syntax: '""Value""') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void TestAttributeWithOptionalParameterNotPassed(bool withParentheses) + { + string attribute = withParentheses ? "My()" : "My"; + + string attributeListSyntax = $"[/**/{attribute}/**/]"; + + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(string value = """") { } +} +" + attributeListSyntax + @" +class C +{ +} +"; + string expectedOperationTree = $@" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '{attribute}') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String value = """"])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: '{attribute}') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '{attribute}') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: """", IsImplicit) (Syntax: '{attribute}') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithUnorderedArguments() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int a, int b) { } +} + +[/**/My(b: 1, a: 0)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(b: 1, a: 0)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 a, System.Int32 b)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(b: 1, a: 0)') + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'a: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestAttributeWithUnorderedArgumentsAndOptionalParameters() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int a, int b, int c = 2, int d = 3) { } +} + +[/**/My(b: 1, a: 0, d: 5)/**/] +class C +{ +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(b: 1, a: 0, d: 5)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 a, System.Int32 b, [System.Int32 c = 2], [System.Int32 d = 3])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + Arguments(4): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'a: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: d) (OperationKind.Argument, Type: null) (Syntax: 'd: 5') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: 'My(b: 1, a: 0, d: 5)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestConversion() + { + string source = @" +using System; + + +[/**/My(0.0f)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(double x) { } +} +"; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(0.0f)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Double x)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(0.0f)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '0.0f') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, Constant: 0, IsImplicit) (Syntax: '0.0f') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Single, Constant: 0) (Syntax: '0.0f') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void SwitchExpression_Attribute() + { + string source = @" +using System; +class Program +{ + [/**/My(1 switch { 1 => 1, _ => 2 })/**/] + public static void M1() { } +} +public class MyAttribute : Attribute +{ + public MyAttribute(int Value) { } +} +public class A +{ + public static implicit operator int(A a) => 4; +} +public class B +{ + public static implicit operator int(B b) => 2; +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (5,19): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [/**/My(1 switch { 1 => 1, _ => 2 })/**/] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => 1, _ => 2 }").WithLocation(5, 19), + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(1 switch ... , _ => 2 })') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 Value)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My(1 switch ... , _ => 2 })') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Value) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') + ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + Arms(2): + ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => 1') + Pattern: + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => 2') + Pattern: + IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void BadAttributeParameterType() + { + string source = @" +[/**/Boom/**/] +class Boom : System.Attribute +{ + public Boom(int? x = 0) { } + + static void Main() + { + typeof(Boom).GetCustomAttributes(true); + } +}"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (2,2): error CS0181: Attribute constructor parameter 'x' has type 'int?', which is not a valid attribute parameter type + // [/**/Boom/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Boom").WithArguments("x", "int?").WithLocation(2, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'Boom') + IObjectCreationOperation (Constructor: Boom..ctor([System.Int32? x = 0])) (OperationKind.ObjectCreation, Type: Boom, IsInvalid, IsImplicit) (Syntax: 'Boom') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'Boom') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'Boom') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'Boom') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void BadAttributeParameterType2() + { + string source = @" +[/**/Boom(null)/**/] +class Boom : System.Attribute +{ + public Boom(int? x = 0) { } + + static void Main() + { + typeof(Boom).GetCustomAttributes(true); + } +}"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (2,2): error CS0181: Attribute constructor parameter 'x' has type 'int?', which is not a valid attribute parameter type + // [/**/Boom/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Boom").WithArguments("x", "int?").WithLocation(2, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'Boom(null)') + IObjectCreationOperation (Constructor: Boom..ctor([System.Int32? x = 0])) (OperationKind.ObjectCreation, Type: Boom, IsInvalid, IsImplicit) (Syntax: 'Boom(null)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithExplicitNullArgument() + { + string source = @" +using System; + +[/**/My(null)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(null)') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(null)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: opt) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Type, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithDefaultNullArgument() + { + string source = @" +using System; + +[/**/My/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: opt) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Type, Constant: null, IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeWithTypeOfArgument() + { + string source = @" +using System; + +[/**/My(typeof(MyAttribute))/**/] +class MyAttribute : Attribute +{ + public MyAttribute(Type opt = null) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(typeof(MyAttribute))') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.Type opt = null])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(typeof(MyAttribute))') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: opt) (OperationKind.Argument, Type: null) (Syntax: 'typeof(MyAttribute)') + ITypeOfOperation (OperationKind.TypeOf, Type: System.Type) (Syntax: 'typeof(MyAttribute)') + TypeOperand: MyAttribute + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void InvalidValue() + { + string source = @" +using System.Security.Permissions; + +[/**/A/**/] +class A : CodeAccessSecurityAttribute +{ + public A(SecurityAction a = 0) : base(a) + { + } + +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (4,12): error CS7049: Security attribute 'A' has an invalid SecurityAction value '0' + // [/**/A/**/] + Diagnostic(ErrorCode.ERR_SecurityAttributeInvalidAction, "A").WithArguments("A", "0").WithLocation(4, 12), + // (5,7): error CS0534: 'A' does not implement inherited abstract member 'SecurityAttribute.CreatePermission()' + // class A : CodeAccessSecurityAttribute + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "A").WithArguments("A", "System.Security.Permissions.SecurityAttribute.CreatePermission()").WithLocation(5, 7) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'A') + IObjectCreationOperation (Constructor: A..ctor([System.Security.Permissions.SecurityAction a = (System.Security.Permissions.SecurityAction)0])) (OperationKind.ObjectCreation, Type: A, IsInvalid, IsImplicit) (Syntax: 'A') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: a) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'A') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Security.Permissions.SecurityAction, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'A') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'A') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void InvalidAttributeParameterType() + { + string source = @" +using System; + +[/**/My/**/] +class MyAttribute : Attribute +{ + public MyAttribute(params int[][,] x) { } +} +"; + var expectedDiagnostics = new DiagnosticDescription[] + { + // (4,12): error CS0181: Attribute constructor parameter 'x' has type 'int[][*,*]', which is not a valid attribute parameter type + // [/**/My/**/] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "My").WithArguments("x", "int[][*,*]").WithLocation(4, 12) + }; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor(params System.Int32[][,] x)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: x) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Int32[][,], IsInvalid, IsImplicit) (Syntax: 'My') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid, IsImplicit) (Syntax: 'My') + Initializer: + IArrayInitializerOperation (0 elements) (OperationKind.ArrayInitializer, Type: null, IsInvalid, IsImplicit) (Syntax: 'My') + Element Values(0) + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Theory] + [InlineData("assembly")] + [InlineData("module")] + public void AssemblyAndModuleAttributeTargets(string attributeTarget) + { + string source = $""" + using System; + + [{attributeTarget}: /**/CLSCompliant(true)/**/] + """; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'CLSCompliant(true)') + IObjectCreationOperation (Constructor: System.CLSCompliantAttribute..ctor(System.Boolean isCompliant)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: 'CLSCompliant(true)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = attributeTarget switch + { + "assembly" => DiagnosticDescription.None, + "module" => new DiagnosticDescription[] + { + // (3,20): warning CS3012: You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking + // [module: /**/CLSCompliant(true)/**/] + Diagnostic(ErrorCode.WRN_CLS_NotOnModules, "CLSCompliant(true)").WithLocation(3, 20), + }, + _ => throw TestExceptionUtilities.UnexpectedValue(attributeTarget), + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ReturnAttributeTarget() + { + string source = """ + using System; + + class MyAttribute : Attribute + { + public MyAttribute(int i) + { + } + } + + public class C + { + [return: /**/My(10)/**/] + public string M() => null; + } + """; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ComplexAttribute() + { + string source = @" +using System; + +[/**/My(i: 1, b: true, o: 2)/**/] +class MyAttribute : Attribute +{ + public MyAttribute(bool b, int i, params object[] o) + { + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: true, o: 2)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: true, o: 2)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ComplexAttributeWithNamedArgument() + { + string source = @" +using System; + +[My(i: 1, b: true, o: 2)] +[/**/My(i: 1, b: true, o: 2, B = 10, D = 5)/**/] +[My(i: 1, b: true, o: 2)] +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute(bool b, int i, params object[] o) + { + } + + public int B { get; set; } + public int C { get; set; } + public int D { get; set; } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializers(2): + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'B = 10') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.B { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'B') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'D = 5') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.D { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'D') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') +"; + string expectedFlowGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (4) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Value: + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Boolean b, System.Int32 i, params System.Object[] o)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Arguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: 'i: 1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: b) (OperationKind.Argument, Type: null) (Syntax: 'b: true') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: o) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Initializer: + IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Element Values(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '2') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'B = 10') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.B { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'B') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'D = 5') + Left: + IPropertyReferenceOperation: System.Int32 MyAttribute.D { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'D') + Instance Receiver: + null + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') + IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: MyAttribute, IsImplicit) (Syntax: 'My(i: 1, b: ... 10, D = 5)') + Next (Regular) Block[B2] + Leaving: {R1} +} +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact] + public void AttributeOnLocalFunction() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + local(); + + [/**/My(10)/**/] + void local() { } + } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnBackingField() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } + + [field: /**/My(10)/**/] + public string S { get; } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void PropertyAttributeTargetOnRecordPositionalParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +record R([property: /**/My(10)/**/] string S); +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics, targetFramework: TargetFramework.Net50); + } + + [Fact] + public void AttributeOnInvalidLocation() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +class C +{ + void M() + { + [/**/My(10)/**/] + int x = 5; + _ = x; + } +} +"; + var expectedDiagnostics = new[] + { + // (15,9): error CS7014: Attributes are not valid in this context. + // [/**/My(10)/**/] + Diagnostic(ErrorCode.ERR_AttributesNotAllowed, "[/**/My(10)/**/]").WithLocation(15, 9), + }; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null, IsInvalid) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsInvalid, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10, IsInvalid) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnEnumMember() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +enum E +{ + [/**/My(10)/**/] + A, +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameter() + { + string source = @" +using System; + +class MyAttribute : Attribute +{ + public MyAttribute(int i) + { + } +} + +class C <[/**/My(10)/**/] T> +{ +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: MyAttribute..ctor(System.Int32 i)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameterWithCallerMemberName_Method() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string s = ""default"") + { + } +} + +class C +{ + void M<[/**/My/**/] T>() { } +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String s = ""default""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""M"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void AttributeOnTypeParameterWithCallerMemberName_Class() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +class MyAttribute : Attribute +{ + public MyAttribute([CallerMemberName] string s = ""default"") + { + } +} + +class C<[/**/My/**/] T> +{ +} +"; + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My') + IObjectCreationOperation (Constructor: MyAttribute..ctor([System.String s = ""default""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: s) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""default"", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"; + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs index e2ba4e27fec38..08a631493ca74 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.cs @@ -34,11 +34,17 @@ void M() } "; string expectedOperationTree = @" -IOperation: (OperationKind.None, Type: System.Diagnostics.ConditionalAttribute) (Syntax: 'Conditional(field)') - Children(1): - IFieldReferenceOperation: System.String C.field (Static) (OperationKind.FieldReference, Type: System.String, Constant: ""field"") (Syntax: 'field') - Instance Receiver: - null +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'Conditional(field)') + IObjectCreationOperation (Constructor: System.Diagnostics.ConditionalAttribute..ctor(System.String conditionString)) (OperationKind.ObjectCreation, Type: System.Diagnostics.ConditionalAttribute, IsImplicit) (Syntax: 'Conditional(field)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: conditionString) (OperationKind.Argument, Type: null) (Syntax: 'field') + IFieldReferenceOperation: System.String C.field (Static) (OperationKind.FieldReference, Type: System.String, Constant: ""field"") (Syntax: 'field') + Instance Receiver: + null + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj b/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj index 44a495ff26660..db9bcc479afa8 100644 --- a/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/CSharp/Test/Semantic/Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj b/src/Compilers/CSharp/Test/Semantic/Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj index 1c392ec2d10dd..8ff52a5e09b57 100644 --- a/src/Compilers/CSharp/Test/Semantic/Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Semantic/Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ArglistTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ArglistTests.cs index 18bb030c0cefb..34f43bd675692 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ArglistTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ArglistTests.cs @@ -229,7 +229,7 @@ static void Main() // (9,40): error CS1510: A ref or out value must be an assignable variable // TypedReference tr3 = __makeref(123); // CS1510 Diagnostic(ErrorCode.ERR_RefLvalueExpected, "123").WithLocation(9, 40), - // (10,40): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (10,40): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // TypedReference tr4 = __makeref(P); // CS0206 Diagnostic(ErrorCode.ERR_RefProperty, "P").WithLocation(10, 40), // (11,40): error CS0199: A static readonly field cannot be used as a ref or out value (except in a static constructor) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index f34e3136c8b07..fe990aa83ede6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -8276,15 +8276,15 @@ .maxstack 2 var variables = nodes.OfType().Where(v => v.Identifier.Text == "d").ToArray(); Assert.Equal(3, variables.Length); - VerifyLocalDelegateType(model, variables[0], "T .Invoke(ref T)"); - VerifyLocalDelegateType(model, variables[1], "U .Invoke(ref U)"); - VerifyLocalDelegateType(model, variables[2], "System.Double .Invoke(ref System.Double)"); + VerifyLocalDelegateType(model, variables[0], "T .Invoke(ref T arg)"); + VerifyLocalDelegateType(model, variables[1], "U .Invoke(ref U arg)"); + VerifyLocalDelegateType(model, variables[2], "System.Double .Invoke(ref System.Double arg)"); var identifiers = nodes.OfType().Where(i => i.Expression is IdentifierNameSyntax id && id.Identifier.Text == "Report").Select(i => i.ArgumentList.Arguments[0].Expression).ToArray(); Assert.Equal(3, identifiers.Length); - VerifyExpressionType(model, identifiers[0], " d", "T .Invoke(ref T)"); - VerifyExpressionType(model, identifiers[1], " d", "U .Invoke(ref U)"); - VerifyExpressionType(model, identifiers[2], " d", "System.Double .Invoke(ref System.Double)"); + VerifyExpressionType(model, identifiers[0], " d", "T .Invoke(ref T arg)"); + VerifyExpressionType(model, identifiers[1], " d", "U .Invoke(ref U arg)"); + VerifyExpressionType(model, identifiers[2], " d", "System.Double .Invoke(ref System.Double arg)"); } [Fact] @@ -10234,6 +10234,168 @@ unsafe static Delegate M2(T t, U u) "); } + [Fact, WorkItem(64436, "https://github.com/dotnet/roslyn/issues/64436")] + public void SynthesizedDelegateTypes_NamedArguments_Ref() + { + var source = """ + var lam1 = (ref int x) => { }; + void m1(ref int x) { } + var inferred1 = m1; + var lam2 = (int x, ref int y) => { }; + void m2(int x, ref int y) { } + var inferred2 = m2; + var lam3 = (in int x, int y) => { }; + void m3(in int x, int y) { } + var inferred3 = m3; + var lam4 = (out int x) => { x = 5; }; + void m4(out int x) { x = 5; } + var inferred4 = m4; + + int i = 1; + lam1(arg: ref i); + inferred1(arg: ref i); + lam2(arg1: 10, arg2: ref i); + inferred2(arg1: 10, arg2: ref i); + lam3(arg1: in i, arg2: 10); + inferred3(arg1: in i, arg2: 10); + lam4(arg: out i); + inferred4(arg: out i); + + // Error cases: + lam1(); + inferred1(); + lam2(10); + inferred2(10); + lam3(arg2: 100); + inferred3(arg2: 100); + lam4(arg: i); + inferred4(arg: i); + """; + CreateCompilation(source).VerifyDiagnostics( + // (25,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg' of '' + // lam1(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam1").WithArguments("arg", "").WithLocation(25, 1), + // (26,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg' of '' + // inferred1(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred1").WithArguments("arg", "").WithLocation(26, 1), + // (27,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // lam2(10); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam2").WithArguments("arg2", "").WithLocation(27, 1), + // (28,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // inferred2(10); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred2").WithArguments("arg2", "").WithLocation(28, 1), + // (29,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // lam3(arg2: 100); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam3").WithArguments("arg1", "").WithLocation(29, 1), + // (30,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // inferred3(arg2: 100); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred3").WithArguments("arg1", "").WithLocation(30, 1), + // (31,11): error CS1620: Argument 1 must be passed with the 'out' keyword + // lam4(arg: i); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("1", "out").WithLocation(31, 11), + // (32,16): error CS1620: Argument 1 must be passed with the 'out' keyword + // inferred4(arg: i); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("1", "out").WithLocation(32, 16)); + } + + [Fact, WorkItem(64436, "https://github.com/dotnet/roslyn/issues/64436")] + public void SynthesizedDelegateTypes_NamedArguments_Pointer() + { + var source = """ + unsafe + { + var lam1 = (int* x) => { }; + void m1(int* x) { } + var inferred1 = m1; + var lam2 = (int x, int* y, int z) => { }; + void m2(int x, int* y, int z) { } + var inferred2 = m2; + + lam1(arg: null); + inferred1(arg: null); + lam2(arg1: 10, arg2: null, arg3: 100); + inferred2(arg1: 10, arg2: null, arg3: 100); + + // Error cases: + lam1(); + inferred1(); + lam2(10); + inferred2(10); + lam2(10, arg3: 100); + inferred2(10, arg3: 100); + } + """; + CreateCompilation(source, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (16,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg' of '' + // lam1(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam1").WithArguments("arg", "").WithLocation(16, 5), + // (17,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg' of '' + // inferred1(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred1").WithArguments("arg", "").WithLocation(17, 5), + // (18,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // lam2(10); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam2").WithArguments("arg2", "").WithLocation(18, 5), + // (19,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // inferred2(10); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred2").WithArguments("arg2", "").WithLocation(19, 5), + // (20,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // lam2(10, arg3: 100); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam2").WithArguments("arg2", "").WithLocation(20, 5), + // (21,5): error CS7036: There is no argument given that corresponds to the required parameter 'arg2' of '' + // inferred2(10, arg3: 100); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred2").WithArguments("arg2", "").WithLocation(21, 5)); + } + + [Fact, WorkItem(64436, "https://github.com/dotnet/roslyn/issues/64436")] + public void SynthesizedDelegateTypes_NamedArguments_MoreThan16Parameters() + { + var range = Enumerable.Range(1, 17); + var manyParams = string.Join(", ", range.Select(i => $"int p{i}")); + var manyArgs = string.Join(", ", range.Select(i => $"arg{i}: {i * 10}")); + var manyTypes = string.Join(",", range.Select(_ => "System.Int32")); + var source = $$""" + using System; + + var lam = ({{manyParams}}) => { }; + void method({{manyParams}}) { } + var inferred = method; + lam({{manyArgs}}); + inferred({{manyArgs}}); + Report(lam); + Report(inferred); + + static void Report(Delegate d) => Console.WriteLine(d.GetType()); + """; + CompileAndVerify(source, expectedOutput: $""" + <>A`17[{manyTypes}] + <>A`17[{manyTypes}] + """).VerifyDiagnostics(); + + var fewArgs = string.Join(", ", range.Skip(1).Select(i => $"arg{i}: {i * 10}")); + source = $$""" + var lam = ({{manyParams}}) => { }; + void method({{manyParams}}) { } + var inferred = method; + lam({{fewArgs}}); + inferred({{fewArgs}}); + lam(); + inferred(); + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // lam(arg2: 20, arg3: 30, arg4: 40, arg5: 50, arg6: 60, arg7: 70, arg8: 80, arg9: 90, arg10: 100, arg11: 110, arg12: 120, arg13: 130, arg14: 140, arg15: 150, arg16: 160, arg17: 170); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam").WithArguments("arg1", "").WithLocation(4, 1), + // (5,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // inferred(arg2: 20, arg3: 30, arg4: 40, arg5: 50, arg6: 60, arg7: 70, arg8: 80, arg9: 90, arg10: 100, arg11: 110, arg12: 120, arg13: 130, arg14: 140, arg15: 150, arg16: 160, arg17: 170); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred").WithArguments("arg1", "").WithLocation(5, 1), + // (6,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // lam(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "lam").WithArguments("arg1", "").WithLocation(6, 1), + // (7,1): error CS7036: There is no argument given that corresponds to the required parameter 'arg1' of '' + // inferred(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "inferred").WithArguments("arg1", "").WithLocation(7, 1)); + } + private static void VerifyLocalDelegateType(SemanticModel model, VariableDeclaratorSyntax variable, string expectedInvokeMethod) { var expectedBaseType = ((CSharpCompilation)model.Compilation).GetSpecialType(SpecialType.System_MulticastDelegate); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs index f01c983054cbd..4d98b24572d33 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs @@ -1278,7 +1278,7 @@ void M() "; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9); comp.VerifyEmitDiagnostics( - // (8,16): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (8,16): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // M2(out Property); // 1 Diagnostic(ErrorCode.ERR_RefProperty, "Property").WithLocation(8, 16) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 350d74c1d5b2a..2a0d7b30d4893 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -2229,14 +2229,14 @@ public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBool string expectedIl = getIl(); - var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular10); + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.Regular10); verifier.VerifyIL("", expectedIl); - var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.NetCoreApp); + var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.Net50); foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) { - var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular10); + var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.Regular10); verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); verifier.VerifyIL("", expectedIl); } @@ -4734,7 +4734,7 @@ .maxstack 5 IL_002e: ret } ", - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } @@ -5530,7 +5530,7 @@ public ref partial struct DefaultInterpolatedStringHandler GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: true, useBoolReturns: false) }; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (3,11): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. // C.M(() => $"{new S { Field = "Field" }}"); @@ -5540,7 +5540,7 @@ public ref partial struct DefaultInterpolatedStringHandler Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, @"new S { Field = ""Field"" }").WithArguments("interpolated string handlers", "10.0").WithLocation(3, 14) ); - comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"value:Field"); verifier.VerifyIL(@"Program.<>c.<
$>b__0_0()", @" @@ -5655,7 +5655,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL(@"Program.<>c.<
$>b__0_0(bool)", !expression.Contains('+') ? @" @@ -5788,7 +5788,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Func)' and 'C.M(Func)' // C.M(b => @@ -5903,7 +5903,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("", !expression.Contains('+') ? @" @@ -5974,7 +5974,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,19): error CS0029: Cannot implicitly convert type 'string' to 'CustomHandler' // CustomHandler x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; @@ -6060,7 +6060,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,9): error CS0172: Type of conditional expression cannot be determined because 'CustomHandler' and 'string' implicitly convert to one another // var x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; @@ -6087,7 +6087,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); VerifyInterpolatedStringExpression(comp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" value:1 @@ -6144,7 +6144,7 @@ public void SwitchTypes_01(string expression) Console.WriteLine(x); "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; @@ -6171,7 +6171,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("", !expression.Contains('+') ? @" @@ -6379,7 +6379,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; @@ -6506,7 +6506,7 @@ public void PassAsRefWithoutKeyword_02(string expression) void M(ref CustomHandler c) => System.Console.WriteLine(c);"; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (2,3): error CS1620: Argument 1 must be passed with the 'ref' keyword // M($"{1,2:f}Literal"); @@ -13455,10 +13455,10 @@ public static CustomHandler M() Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(17, 19) }; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -13487,7 +13487,7 @@ public static CustomHandler M() } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -13524,10 +13524,10 @@ public static ref CustomHandler M() Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(17, 9) }; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -13564,10 +13564,10 @@ public static ref CustomHandler M() Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, expression).WithLocation(17, 20) }; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -13604,7 +13604,7 @@ public ref struct S1 } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s}"); @@ -13613,7 +13613,7 @@ public ref struct S1 // M2(ref s1, $"{s}"); Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(17, 23)); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s}"); @@ -13662,10 +13662,10 @@ public ref struct S1 } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -13696,10 +13696,10 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -13730,10 +13730,10 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -13767,7 +13767,7 @@ public static ref CustomHandler M2(ref Span s, [InterpolatedStringHandlerA } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (16,16): error CS8352: Cannot use variable 'c' in this context because it may expose referenced variables outside of their declaration scope // return c; @@ -13805,7 +13805,7 @@ public ref struct S1 public CustomHandler Handler; } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (15,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"{s2}"); @@ -13815,7 +13815,7 @@ public ref struct S1 Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(15, 23) ); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (10,100): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { s1.Handler = this; } @@ -13860,10 +13860,10 @@ static void Main() static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) { } }"; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); - comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.NetCoreApp); + comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (5,97): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, ref S s) : this() { s.Handler = this; } @@ -13908,7 +13908,7 @@ static void F2() static void M(ref CustomHandler handler) { } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (20,9): error CS8350: This combination of arguments to 'CustomHandler.AppendFormatted(Span)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // h2.AppendFormatted(s); // 1 @@ -13949,7 +13949,7 @@ static void F2() static void M(ref CustomHandler handler) { } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -13985,7 +13985,7 @@ static CustomHandler F2() } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (15,16): error CS8352: Cannot use variable 'h1' in this context because it may expose referenced variables outside of their declaration scope // return h1; // 1 @@ -14036,7 +14036,7 @@ static CustomHandler F3() } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (29,16): error CS8352: Cannot use variable 'h3' in this context because it may expose referenced variables outside of their declaration scope // return h3; // 1 @@ -14082,7 +14082,7 @@ static CustomHandler F4() } "; // https://github.com/dotnet/roslyn/issues/63306: Should report an error in each case. - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -14122,7 +14122,7 @@ static CustomHandler F4() } } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 8d107c0a61c3a..8cbe991d8c4fd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -3913,10 +3913,8 @@ static void Main() a = (IdentifierNameSyntax)newTree.GetRoot().DescendantNodes().OfType().Single().Name; Assert.Equal("A", a.Identifier.Text); - // If we aren't using the right binder here, the compiler crashes going through the binder factory var info = model.GetSymbolInfo(a); - // This behavior is wrong. See https://github.com/dotnet/roslyn/issues/24135 - Assert.Equal(attrType, info.Symbol); + Assert.Equal(attrCtor, info.Symbol); } [Fact] @@ -6894,5 +6892,63 @@ void Choice(Action a) var model = comp.GetSemanticModel(syntaxTree); AssertEx.Equal("System.Action", model.GetTypeInfo(action).Type.ToTestDisplayString()); } + + [Fact] + [WorkItem(64392, "https://github.com/dotnet/roslyn/issues/64392")] + public void ReferToFieldWithinLambdaInTypeAttribute_01() + { + var source = @" +[Display(x => $""{Name}"")] +public class Test +{ + [Display(Name = ""Name"")] + public string Name { get; } +} + +public class DisplayAttribute : System.Attribute +{ + public DisplayAttribute() { } +} +"; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,2): error CS1729: 'DisplayAttribute' does not contain a constructor that takes 1 arguments + // [Display(x => $"{Name}")] + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"Display(x => $""{Name}"")").WithArguments("DisplayAttribute", "1").WithLocation(2, 2), + // (5,14): error CS0246: The type or namespace name 'Name' could not be found (are you missing a using directive or an assembly reference?) + // [Display(Name = "Name")] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Name").WithArguments("Name").WithLocation(5, 14) + ); + } + + [Fact] + [WorkItem(64392, "https://github.com/dotnet/roslyn/issues/64392")] + public void ReferToFieldWithinLambdaInTypeAttribute_02() + { + var source = @" +[Display(x => Name)] +public class Test +{ + [Display(Name = ""Name"")] + public string Name { get; } +} + +public class DisplayAttribute : System.Attribute +{ + public DisplayAttribute() { } +} +"; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,2): error CS1729: 'DisplayAttribute' does not contain a constructor that takes 1 arguments + // [Display(x => Name)] + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Display(x => Name)").WithArguments("DisplayAttribute", "1").WithLocation(2, 2), + // (5,14): error CS0246: The type or namespace name 'Name' could not be found (are you missing a using directive or an assembly reference?) + // [Display(Name = "Name")] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Name").WithArguments("Name").WithLocation(5, 14) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index af37149cd8be9..26125f169d6d0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -409,10 +409,8 @@ static void M() a = newTree.GetRoot().DescendantNodes().OfType().ElementAt(2); Assert.Equal("A", a.Identifier.Text); - // If we aren't using the right binder here, the compiler crashes going through the binder factory var info = model.GetSymbolInfo(a); - // This behavior is wrong. See https://github.com/dotnet/roslyn/issues/24135 - Assert.Equal(attrType, info.Symbol); + Assert.Equal(attrCtor, info.Symbol); } [Theory] @@ -4843,10 +4841,7 @@ class Test : System.Attribute compilation.VerifyDiagnostics( // (10,23): error CS0103: The name 'b2' does not exist in the current context // [Test(p = b2)] - Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(10, 23), - // (6,20): warning CS0219: The variable 'b1' is assigned but its value is never used - // const bool b1 = true; - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "b1").WithArguments("b1").WithLocation(6, 20) + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(10, 23) ); var tree = compilation.SyntaxTrees.Single(); @@ -8599,6 +8594,7 @@ private void VerifyParameter(CSharpCompilation comp, int index, string expectedM } [Fact] + [WorkItem(60801, "https://github.com/dotnet/roslyn/issues/60801")] public void ParameterScope_InMethodAttributeNameOf_GetSymbolInfoOnSpeculativeMethodBodySemanticModel() { var source = @" diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 47cbbe9156ab2..8dd45eeb0f9bc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -18926,9 +18926,9 @@ void M(in string xIn) "; var c2 = CreateCompilation(new[] { source2 }, options: WithNullableEnable()); c2.VerifyDiagnostics( - // (6,9): error CS8331: Cannot assign to variable 'in string' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to variable 'xIn' or use it as the right hand side of a ref assignment because it is a readonly variable // xIn = null; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "xIn").WithArguments("variable", "in string").WithLocation(6, 9)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "xIn").WithArguments("variable", "xIn").WithLocation(6, 9)); } [Fact] @@ -33580,7 +33580,7 @@ public void M() // (12,9): warning CS8602: Dereference of a possibly null reference. // field.ToString(); // 4 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(12, 9), - // (14,15): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (14,15): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // M(ref Property); // 5 Diagnostic(ErrorCode.ERR_RefProperty, "Property").WithLocation(14, 15), // (15,20): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -127121,13 +127121,13 @@ bool F4([NotNullWhen(true)] out string? s) }"; var comp = CreateNullableCompilation(new[] { source, NotNullWhenAttributeDefinition }); comp.VerifyDiagnostics( - // (13,9): error CS8762: Parameter 's' must have a non-null value when exiting with 'true'. + // (13,9): warning CS8762: Parameter 's' must have a non-null value when exiting with 'true'. // return true; // 1 Diagnostic(ErrorCode.WRN_ParameterConditionallyDisallowsNull, "return true;").WithArguments("s", "true").WithLocation(13, 9), - // (18,9): error CS8331: Cannot assign to variable 'in string?' or use it as the right hand side of a ref assignment because it is a readonly variable + // (18,9): error CS8331: Cannot assign to variable 's' or use it as the right hand side of a ref assignment because it is a readonly variable // s = null; // 2 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s").WithArguments("variable", "in string?").WithLocation(18, 9), - // (25,9): error CS8762: Parameter 's' must have a non-null value when exiting with 'true'. + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s").WithArguments("variable", "s").WithLocation(18, 9), + // (25,9): warning CS8762: Parameter 's' must have a non-null value when exiting with 'true'. // return true; // 3 Diagnostic(ErrorCode.WRN_ParameterConditionallyDisallowsNull, "return true;").WithArguments("s", "true").WithLocation(25, 9) ); @@ -156450,5 +156450,30 @@ public class C Diagnostic(ErrorCode.ERR_NoSuchMember, "B").WithArguments("S", "B").WithLocation(12, 24) ); } + + [Fact, WorkItem(64599, "https://github.com/dotnet/roslyn/issues/64599")] + public void LambdaNullabilityCycle() + { + var source = """ +#nullable enable +using System; +class Program +{ + static void Main() + { + var lam = ([A(nameof(lam))] int x) => { }; + } +} +[AttributeUsage(AttributeTargets.Parameter)] +class A : Attribute +{ + public A(string _) {} +} +"""; + CreateCompilation(source).VerifyDiagnostics( + // (7,30): error CS0841: Cannot use local variable 'lam' before it is declared + // var lam = ([A(nameof(lam))] int x) => { }; + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "lam").WithArguments("lam").WithLocation(7, 30)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs index 2bbf936a2b2e2..61075f1fb3320 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs @@ -3510,7 +3510,7 @@ where node.IsKind(SyntaxKind.CollectionInitializerExpression) Assert.Equal(2, symbolInfo.CandidateSymbols.Length); Assert.Equal(new[] {"void X.Add(System.Collections.Generic.List x)", "void X.Add(X x)"}, - symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString()).Order().ToArray()); + Roslyn.Utilities.EnumerableExtensions.Order(symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString())).ToArray()); } [WorkItem(529787, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529787")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index 690df32f2800d..c73229ed17116 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -36243,6 +36243,182 @@ public static void Test3(this Cls x) {} var node = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "Test3").Last(); Assert.True(model.GetSymbolInfo(node).IsEmpty); } + + [Fact] + [WorkItem(60801, "https://github.com/dotnet/roslyn/issues/60801")] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInAttribute_01() + { + var source = @" +class C +{ + void M() + { + [My(M2(out var x))] + void local(int parameter) { } + } + + static string M2(out int x) => throw null; +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,13): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [My(M2(out var x))] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "M2(out var x)").WithLocation(6, 13), + // (7,14): warning CS8321: The local function 'local' is declared but never used + // void local(int parameter) { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(7, 14) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("M2(out var x)", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(method.Body.SpanStart + 1, method.DescendantNodes().OfType().Single(), out speculativeModel)); + symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + } + + + [Fact] + [WorkItem(60801, "https://github.com/dotnet/roslyn/issues/60801")] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInAttribute_02() + { + var source = @" +class C +{ + void M() + { + [My(() => { [My(M2(out var x))] static string M2(out int x) => throw null; })] + void local(int parameter) { } + } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string name1) { } +} +"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,13): error CS1660: Cannot convert lambda expression to type 'string' because it is not a delegate type + // [My(() => { [My(M2(out var x))] static string M2(out int x) => throw null; })] + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => { [My(M2(out var x))] static string M2(out int x) => throw null; }").WithArguments("lambda expression", "string").WithLocation(6, 13), + // (7,14): warning CS8321: The local function 'local' is declared but never used + // void local(int parameter) { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(7, 14) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("M2(out var x)", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + Assert.Same(symbolInfo.Symbol, speculativeModel.GetDeclaredSymbol(tree2.GetRoot().DescendantNodes().OfType().Where(l => l.Identifier.ValueText == "M2").Single())); + } + + [Fact] + [WorkItem(60801, "https://github.com/dotnet/roslyn/issues/60801")] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInDefaultParameterValue_01() + { + var source = @" +class C +{ + void M() + { + void local(string parameter = M2(out var x)) { } + } + + static string M2(out int x) => throw null; +} +"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,14): warning CS8321: The local function 'local' is declared but never used + // void local(string parameter = M2(out var x)) { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(6, 14), + // (6,39): error CS1736: Default parameter value for 'parameter' must be a compile-time constant + // void local(string parameter = M2(out var x)) { } + Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M2(out var x)").WithArguments("parameter").WithLocation(6, 39) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("M2(out var x)", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + + var equalsValue = method.DescendantNodes().OfType().Single(); + Assert.True(model.TryGetSpeculativeSemanticModel(equalsValue.Value.SpanStart, equalsValue, out speculativeModel)); + symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + } + + [Fact] + [WorkItem(60801, "https://github.com/dotnet/roslyn/issues/60801")] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInDefaultParameterValue_02() + { + var source = @" +class C +{ + void M() + { + void local(string parameter = () => { static string M2(out int x, string y = M2(out var a, ""b"")) => throw null; }) { } + } +} +"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,14): warning CS8321: The local function 'local' is declared but never used + // void local(string parameter = M2(out var x)) { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(6, 14), + // (6,39): error CS1736: Default parameter value for 'parameter' must be a compile-time constant + // void local(string parameter = () => { static string M2(out int x, string y = M2(out var a, "b")) => throw null; }) { } + Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, @"() => { static string M2(out int x, string y = M2(out var a, ""b"")) => throw null; }").WithArguments("parameter").WithLocation(6, 39) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(@"M2(out var a, ""b"")", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String M2(out System.Int32 x, [System.String y = null])", symbolInfo.Symbol.ToTestDisplayString()); + Assert.Same(symbolInfo.Symbol, speculativeModel.GetDeclaredSymbol(tree2.GetRoot().DescendantNodes().OfType().Where(l => l.Identifier.ValueText == "M2").Single())); + } } internal static class OutVarTestsExtensions diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 45f0e8f45e05a..857d5baa359e3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -11097,18 +11097,18 @@ void M(C c, in int y) CreateCompilation(code, references: new[] { libComp.EmitToImageReference() }).VerifyDiagnostics( - // (13,10): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (13,10): error CS8329: Cannot use variable 'y' as a ref or out value because it is a readonly variable // y.R_extension(); // error 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(13, 10), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "y").WithLocation(13, 10), // (14,10): error CS1510: A ref or out value must be an assignable variable // 1.R_extension(); // error 2 Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(14, 10) ); CreateCompilation(code, references: new[] { libComp.ToMetadataReference() }).VerifyDiagnostics( - // (13,10): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (13,10): error CS8329: Cannot use variable 'y' as a ref or out value because it is a readonly variable // y.R_extension(); // error 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(13, 10), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "y").WithLocation(13, 10), // (14,10): error CS1510: A ref or out value must be an assignable variable // 1.R_extension(); // error 2 Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(14, 10) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 3be739ff34b48..4caa953e70f09 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -624,14 +624,14 @@ public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBool string expectedIl = getIl(); - var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.RegularPreview); + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.RegularPreview); verifier.VerifyIL("", expectedIl); - var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.NetCoreApp); + var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.Net50); foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) { - var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.RegularPreview); + var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.Net50, parseOptions: TestOptions.RegularPreview); verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); verifier.VerifyIL("", expectedIl); } @@ -3059,7 +3059,7 @@ .maxstack 5 IL_002e: ret } ", - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } @@ -3769,7 +3769,7 @@ public ref partial struct DefaultInterpolatedStringHandler GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: true, useBoolReturns: false) }; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net50); if (expression.Contains('+')) { @@ -3912,7 +3912,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL(@"Program.<>c.<
$>b__0_0(bool)", !expression.Contains('+') ? @" @@ -4045,7 +4045,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Func)' and 'C.M(Func)' // C.M(b => @@ -4183,7 +4183,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("", !expression.Contains('+') ? @" @@ -4254,7 +4254,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,19): error CS0029: Cannot implicitly convert type 'string' to 'CustomHandler' // CustomHandler x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; @@ -4339,7 +4339,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,9): error CS0172: Type of conditional expression cannot be determined because 'CustomHandler' and 'string' implicitly convert to one another // var x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; @@ -4422,7 +4422,7 @@ public void SwitchTypes_01(string expression) Console.WriteLine(x); "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; @@ -4448,7 +4448,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); verifier.VerifyIL("", !expression.Contains('+') ? @" @@ -4656,7 +4656,7 @@ public partial struct CustomHandler } "; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (4,29): error CS8506: No best type was found for the switch expression. // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; @@ -4782,7 +4782,7 @@ public void PassAsRefWithoutKeyword_02(string expression) void M(ref CustomHandler c) => System.Console.WriteLine(c);"; - var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (2,3): error CS1620: Argument 1 must be passed with the 'ref' keyword // M($"{1,2:f}Literal"); @@ -10981,7 +10981,7 @@ public static CustomHandler M() } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,19): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope // return $"{s}"; @@ -11016,7 +11016,7 @@ public static ref CustomHandler M() } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8150: By-value returns may only be used in methods that return by value // return $"{s}"; @@ -11051,7 +11051,7 @@ public static ref CustomHandler M() } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,20): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return ref $"{s}"; @@ -11093,7 +11093,7 @@ public ref struct S1 } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope // M2(ref s1, $"""{s}""" + $""" @@ -11138,7 +11138,7 @@ public ref struct S1 } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -11171,7 +11171,7 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -11204,7 +11204,7 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics(); } @@ -11238,7 +11238,7 @@ public static ref CustomHandler M2(ref Span s, [InterpolatedStringHandlerA } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (18,16): error CS8352: Cannot use variable 'c' in this context because it may expose referenced variables outside of their declaration scope // return c; @@ -11276,7 +11276,7 @@ public ref struct S1 } "; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (10,107): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, scoped ref S1 s1) : this() { s1.Handler = this; } @@ -11314,7 +11314,7 @@ static void Main() static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) { } }"; - var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.Net50); comp.VerifyDiagnostics( // (5,104): error CS8352: Cannot use variable 'out CustomHandler' in this context because it may expose referenced variables outside of their declaration scope // public CustomHandler(int literalLength, int formattedCount, scoped ref S s) : this() { s.Handler = this; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index c700404102d9b..61b1787d54b17 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -6530,7 +6530,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index e1b098821d06d..7f78f76399798 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -26997,7 +26997,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; @@ -27008,7 +27008,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27022,7 +27022,7 @@ private void Handle(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); @@ -27131,7 +27131,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); RegisterOperationAction(context); @@ -27144,7 +27144,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27160,7 +27160,7 @@ private void Handle(OperationBlockStartAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); @@ -27205,7 +27205,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString()); break; @@ -27216,7 +27216,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind); @@ -27230,7 +27230,7 @@ private void Handle6(OperationBlockAnalysisContext context) Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind); Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString()); - Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind); + Assert.Equal(OperationKind.Attribute, context.OperationBlocks[1].Kind); Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString()); Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 6b8065380cae2..a458af53e721c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -4952,7 +4952,7 @@ static RS M3(scoped RS rs3) Diagnostic(ErrorCode.ERR_CallArgMixing, "M0(rs3, out rs4)").WithArguments("Program.M0(RS, out RS)", "rs1").WithLocation(28, 9), // (28,12): error CS8352: Cannot use variable 'scoped RS' in this context because it may expose referenced variables outside of their declaration scope // M0(rs3, out rs4); // 3 - Diagnostic(ErrorCode.ERR_EscapeVariable, "rs3").WithArguments("RS").WithLocation(28, 12)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs3").WithArguments("scoped RS").WithLocation(28, 12)); } [Fact] @@ -5208,37 +5208,42 @@ static RS M2(ref RS rs5) M0(ref rs5, out scoped RS rs6); return rs6; // 2 } + + static RS M12(ref RS rs3) + { + // RSTE of rs3 is ReturnOnly. + // However, since rs4 is 'scoped', its STE should be narrowed to CurrentMethod + scoped RS rs4; + M0(ref rs3, out rs4); + return rs4; // 3 + } + + static RS M22(ref RS rs5) + { + // RSTE of rs5 is ReturnOnly. + // However, since rs6 is 'scoped', its STE should be narrowed to CurrentMethod + scoped RS rs6; + M0(ref rs5, out rs6); + return rs6; // 4 + } } """; - // Once 'out scoped var' support is added, this test baseline should be updated. - // https://github.com/dotnet/roslyn/issues/64556 var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (18,25): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // M0(ref rs3, out scoped var rs4); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(18, 25), - // (18,36): error CS1003: Syntax error, ',' expected - // M0(ref rs3, out scoped var rs4); - Diagnostic(ErrorCode.ERR_SyntaxError, "rs4").WithArguments(",").WithLocation(18, 36), - // (18,36): error CS0103: The name 'rs4' does not exist in the current context - // M0(ref rs3, out scoped var rs4); - Diagnostic(ErrorCode.ERR_NameNotInContext, "rs4").WithArguments("rs4").WithLocation(18, 36), - // (19,16): error CS0103: The name 'rs4' does not exist in the current context + // (19,16): error CS8352: Cannot use variable 'rs4' in this context because it may expose referenced variables outside of their declaration scope // return rs4; // 1 - Diagnostic(ErrorCode.ERR_NameNotInContext, "rs4").WithArguments("rs4").WithLocation(19, 16), - // (26,25): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // M0(ref rs5, out scoped RS rs6); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(26, 25), - // (26,35): error CS1003: Syntax error, ',' expected - // M0(ref rs5, out scoped RS rs6); - Diagnostic(ErrorCode.ERR_SyntaxError, "rs6").WithArguments(",").WithLocation(26, 35), - // (26,35): error CS0103: The name 'rs6' does not exist in the current context - // M0(ref rs5, out scoped RS rs6); - Diagnostic(ErrorCode.ERR_NameNotInContext, "rs6").WithArguments("rs6").WithLocation(26, 35), - // (27,16): error CS0103: The name 'rs6' does not exist in the current context + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs4").WithArguments("rs4").WithLocation(19, 16), + // (27,16): error CS8352: Cannot use variable 'rs6' in this context because it may expose referenced variables outside of their declaration scope // return rs6; // 2 - Diagnostic(ErrorCode.ERR_NameNotInContext, "rs6").WithArguments("rs6").WithLocation(27, 16)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs6").WithArguments("rs6").WithLocation(27, 16), + // (36,16): error CS8352: Cannot use variable 'rs4' in this context because it may expose referenced variables outside of their declaration scope + // return rs4; // 3 + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs4").WithArguments("rs4").WithLocation(36, 16), + // (45,16): error CS8352: Cannot use variable 'rs6' in this context because it may expose referenced variables outside of their declaration scope + // return rs6; // 4 + Diagnostic(ErrorCode.ERR_EscapeVariable, "rs6").WithArguments("rs6").WithLocation(45, 16) + ); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs index 2a335ffc1f439..53ec9f4be101a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs @@ -1497,9 +1497,9 @@ public static void Main() }"; CreateCompilationWithMscorlib40AndSystemCore(code).VerifyDiagnostics( - // (6,9): error CS8408: Cannot assign to variable 'in int' or use it as the right hand side of a ref assignment because it is a readonly variable + // (6,9): error CS8331: Cannot assign to variable 'p' or use it as the right hand side of a ref assignment because it is a readonly variable // p++; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "p").WithArguments("variable", "in int").WithLocation(6, 9)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "p").WithArguments("variable", "p").WithLocation(6, 9)); } [Fact] @@ -2125,9 +2125,9 @@ public static void Ref(ref int p) }"; CreateCompilationWithMscorlib40AndSystemCore(code).VerifyDiagnostics( - // (6,17): error CS8406: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (6,17): error CS8329: Cannot use variable 'p' as a ref or out value because it is a readonly variable // Ref(ref p); // Should be an error - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "p").WithArguments("variable", "in int").WithLocation(6, 17)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "p").WithArguments("variable", "p").WithLocation(6, 17)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 3252bdfc7714c..663a2975eceea 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -20,24 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class RefFieldTests : CSharpTestBase { - private static bool IsNet70OrGreater() - { -#if NET7_0_OR_GREATER - return true; -#else - return false; -#endif - } - - private static string IncludeExpectedOutput(string expectedOutput) => IsNet70OrGreater() ? expectedOutput : null; - - private static IEnumerable CopyWithoutSharingCachedSymbols(IEnumerable references) - { - foreach (var reference in references) - { - yield return ((AssemblyMetadata)((MetadataImageReference)reference).GetMetadata()).CopyWithoutSharingCachedSymbols().GetReference(); - } - } + private static string IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null; [CombinatorialData] [Theory] @@ -99,12 +82,7 @@ void M5(S s5) M2(ref s5.F1); } }"; - var mscorlibRefWithRefFields = GetMscorlibRefWithoutSharingCachedSymbols(); - - // Note: we use skipExtraValidation so that nobody pulls - // on the compilation or its references before we set the RuntimeSupportsByRefFields flag. - var comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10, skipExtraValidation: true); - comp.Assembly.RuntimeSupportsByRefFields = true; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (3,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // public ref T F1; @@ -116,7 +94,7 @@ void M5(S s5) // scoped S s2; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(25, 9)); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }); + comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); @@ -145,7 +123,7 @@ static void M3(S s) } }"; - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (8,25): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // s1 = new S { F1 = t }; @@ -169,7 +147,7 @@ static void M3(S s) VerifyFieldSymbol(comp.GetMember("S.F1"), "ref T S.F1", RefKind.Ref, new string[0]); VerifyFieldSymbol(comp.GetMember("S.F2"), "ref readonly T S.F2", RefKind.RefReadOnly, new string[0]); - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular11); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); VerifyFieldSymbol(comp.GetMember("S.F1"), "ref T S.F1", RefKind.Ref, new string[0]); @@ -186,14 +164,7 @@ public void RefField(bool useCompilationReference) public ref T F; public S(ref T t) { F = ref t; } }"; - // Avoid sharing mscorlib symbols with other tests since we are about to change - // RuntimeSupportsByRefFields property for it. - var mscorlibRefWithRefFields = GetMscorlibRefWithoutSharingCachedSymbols(); - - // Note: we use skipExtraValidation so that nobody pulls - // on the compilation or its references before we set the RuntimeSupportsByRefFields flag. - var comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10, skipExtraValidation: true); - comp.Assembly.RuntimeSupportsByRefFields = true; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (3,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // public ref T F; @@ -201,7 +172,7 @@ public void RefField(bool useCompilationReference) VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }); + comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); @@ -223,7 +194,7 @@ static void Main() } }"; - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (8,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // s.F = 2; @@ -237,7 +208,7 @@ static void Main() VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -261,14 +232,8 @@ public S(in T t) F = ref t; } }"; - // Avoid sharing mscorlib symbols with other tests since we are about to change - // RuntimeSupportsByRefFields property for it. - var mscorlibRefWithRefFields = GetMscorlibRefWithoutSharingCachedSymbols(); - // Note: we use skipExtraValidation so that nobody pulls - // on the compilation or its references before we set the RuntimeSupportsByRefFields flag. - var comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10, skipExtraValidation: true); - comp.Assembly.RuntimeSupportsByRefFields = true; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (3,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // public ref readonly T F; @@ -276,7 +241,7 @@ public S(in T t) VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); - comp = CreateEmptyCompilation(sourceA, references: new[] { mscorlibRefWithRefFields }); + comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); @@ -303,7 +268,7 @@ static void Main() } }"; - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular10); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // s.F.G = 2; @@ -317,7 +282,7 @@ static void Main() VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); - comp = CreateEmptyCompilation(sourceB, references: new[] { refA, mscorlibRefWithRefFields }, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -380,6 +345,148 @@ ref struct B VerifyFieldSymbol(field, "ref readonly System.Int32 A.F", RefKind.RefReadOnly, new string[0]); } + [WorkItem(64682, "https://github.com/dotnet/roslyn/issues/64682")] + [Fact] + public void RefFieldInNonRefStruct() + { + var sourceA = +@".class public A +{ + .field public !0& F1 +} +.class public sealed S extends [mscorlib]System.ValueType +{ + .field public int32 F2 +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static ref int F1(ref A a) => ref a.F1; // 1 + static ref int F2(ref S s) => ref s.F2; // 2 +}"; + // https://github.com/dotnet/roslyn/issues/64682: Should report use-site errors. + var comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, verify: Verification.Skipped); + } + + [Fact] + public void RefAndReadonlyRefStruct_01() + { + var source = +@"#pragma warning disable 169 +ref struct A +{ + ref int A1; + ref readonly int A2; + readonly ref int A3; + readonly ref readonly int A4; +} +readonly ref struct B +{ + ref int B1; + ref readonly int B2; + readonly ref int B3; + readonly ref readonly int B4; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (11,13): error CS8340: Instance fields of readonly structs must be readonly. + // ref int B1; + Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "B1").WithLocation(11, 13), + // (12,22): error CS8340: Instance fields of readonly structs must be readonly. + // ref readonly int B2; + Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "B2").WithLocation(12, 22)); + } + + /// + /// ref readonly fields emitted as initonly. + /// ref readonly fields emitted with System.Runtime.CompilerServices.IsReadOnlyAttribute. + /// + [Fact] + public void RefAndReadonlyRefStruct_02() + { + var source = +@"#pragma warning disable 169 +ref struct A +{ + ref int A1; + ref readonly int A2; + readonly ref int A3; + readonly ref readonly int A4; +} +readonly ref struct B +{ + readonly ref int B3; + readonly ref readonly int B4; +}"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyTypeIL("A", +@".class private sequential ansi sealed beforefieldinit A + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 0a 52 65 66 53 74 72 75 63 74 73 00 00 + ) + // Fields + .field private int32& A1 + .field private int32& A2 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .field private initonly int32& A3 + .field private initonly int32& A4 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) +} // end of class A +"); + verifier.VerifyTypeIL("B", +@".class private sequential ansi sealed beforefieldinit B + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 0a 52 65 66 53 74 72 75 63 74 73 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private initonly int32& B3 + .field private initonly int32& B4 + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) +} // end of class B +"); + } + [Fact] public void TupleField() { @@ -454,7 +561,7 @@ public void FixedField_01() public fixed ref int F1[3]; public fixed ref readonly int F2[3]; }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (3,26): error CS9049: A fixed field must not be a ref field. // public fixed ref int F1[3]; @@ -492,7 +599,7 @@ unsafe static void Main() Console.WriteLine(s.F[1]); } }"; - var comp = CreateCompilation(sourceB, references: new[] { refA }, options: TestOptions.UnsafeReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, references: new[] { refA }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,29): error CS0570: 'S.F' is not supported by the language // Console.WriteLine(s.F[1]); @@ -522,7 +629,7 @@ static void Main() Console.WriteLine(r.F2); } }"; - var comp = CreateCompilation(sourceB, references: new[] { refA }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,29): error CS0570: 'R.F1' is not supported by the language // Console.WriteLine(r.F1); @@ -546,7 +653,7 @@ ref struct R volatile ref int _v1; volatile ref readonly int _v2; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,20): error CS0106: The modifier 'static' is not valid for this item // static ref int _s1; @@ -604,7 +711,7 @@ unsafe void M2( } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (14,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // S* s, // 1 @@ -650,7 +757,7 @@ unsafe class C C* M3() => (C*)null; // 2 } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (14,9): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // S2* M2() => (S2*)null; // 1 @@ -699,7 +806,7 @@ unsafe C M4() } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (20,9): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // S2* s2 = null; // 1 @@ -782,7 +889,7 @@ unsafe void M5() // A fixed_pointer_initializer can be one of the following: // The token “&” followed by a variable_reference to a moveable variable of type T, // provided the type T* is implicitly convertible to the pointer type given in the fixed statement. - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (23,16): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // fixed (S2* x2 = &Prop2) { } // 1 @@ -880,7 +987,7 @@ unsafe void M2(void* p) } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // void M(void* p) @@ -973,7 +1080,7 @@ unsafe int M5() } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (19,23): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // return sizeof(S2*); // 1 @@ -1038,7 +1145,7 @@ unsafe object M3() } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (19,23): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // return typeof(S2*); // 1 @@ -1114,7 +1221,7 @@ void M3(ref S2 s) } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (15,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var x = &s; // 1 @@ -1157,7 +1264,7 @@ unsafe class C2 } } "; - comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (11,17): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // var x = &s; // 1 @@ -1194,7 +1301,7 @@ unsafe class C2 } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,25): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // return (IntPtr*)&s; // 1 @@ -1228,7 +1335,7 @@ unsafe static void Assign(IntPtr* p, ref S2 r) } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (10,11): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // *(S2*)p = r; @@ -1273,7 +1380,7 @@ int M2(S2 s) } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (19,17): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // return (&s)->F1; // 2 @@ -1318,7 +1425,7 @@ S2 M2(S2 s) } } "; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (19,17): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // return (&s)[0]; // 1 @@ -1366,7 +1473,7 @@ unsafe void M() "; var spanReference = CreateCompilation(SpanSource, options: TestOptions.UnsafeReleaseDll); - var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (16,29): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('S2') // var x2 = stackalloc S2[0]; // 1 @@ -1422,7 +1529,7 @@ unsafe C M2() "; var spanReference = CreateCompilation(SpanSource, options: TestOptions.UnsafeReleaseDll); - var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (16,18): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('S2') // var x2 = stackalloc[] { new S2() }; // 1 @@ -1474,7 +1581,7 @@ unsafe void M() "; var spanReference = CreateCompilation(SpanSource, options: TestOptions.UnsafeReleaseDll); - var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (16,29): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // var x2 = stackalloc S2*[0]; // 1 @@ -1530,7 +1637,7 @@ unsafe void M() "; var spanReference = CreateCompilation(SpanSource, options: TestOptions.UnsafeReleaseDll); - var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, references: new[] { spanReference.EmitToImageReference() }, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (16,19): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('S2') // var s2 = (S2*)null; // 1 @@ -1567,7 +1674,7 @@ public R() } } """; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor")); verifier.VerifyIL("R..ctor()", @" @@ -1642,7 +1749,7 @@ public R() } } """; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor")); @@ -1678,7 +1785,7 @@ public R() } } """; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (7,29): warning CS0649: Field 'R.field' is never assigned to, and will always have its default value 0 // public readonly ref int field; @@ -1721,7 +1828,7 @@ public R() } } """; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular11, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor")); verifier.VerifyIL("R..ctor()", @" @@ -1765,7 +1872,7 @@ static void Main() Console.WriteLine(a.F); } }"; - var comp = CreateCompilation(sourceB, new[] { refA }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,29): error CS0570: 'A.F' is not supported by the language // Console.WriteLine(a.F); @@ -1797,7 +1904,7 @@ static void Main() Console.WriteLine(a.F); } }"; - var comp = CreateCompilation(sourceB, new[] { refA }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,29): error CS0570: 'A.F' is not supported by the language // Console.WriteLine(a.F); @@ -1837,7 +1944,7 @@ static void Main() Console.WriteLine(b.F); } }"; - var comp = CreateCompilation(sourceC, new[] { refB }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceC, new[] { refB }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,29): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly 'A, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // Console.WriteLine(b.F); @@ -1867,7 +1974,7 @@ .field public int8& F3 static object F2() => new R().F2; static int F3() => new R().F3; }"; - var compB = CreateCompilation(sourceB, references: new[] { refA }, runtimeFeature: RuntimeFlag.ByRefFields); + var compB = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(compB, verify: Verification.Skipped); // MemberRefMetadataDecoder.FindFieldBySignature() is used to find fields when realIL: true. verifier.VerifyIL("B.F1", realIL: true, expectedIL: @@ -1935,7 +2042,7 @@ public void NonRefStructContainer(string type) ref int F; }}"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,5): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // ref int F; @@ -1944,7 +2051,7 @@ public void NonRefStructContainer(string type) // ref int F; Diagnostic(ErrorCode.ERR_RefFieldInNonRefStruct, "F").WithLocation(4, 13)); - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,13): error CS9059: A ref field can only be declared in a ref struct. // ref int F; @@ -2099,7 +2206,7 @@ static void F(ref R1 r1) r2.F = ref r1; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (8,12): error CS9050: A ref field cannot refer to a ref struct. // public ref R1 F; @@ -2137,7 +2244,7 @@ static void F(ref R1 r1) r2.F = ref r1; } }"; - var comp = CreateCompilation(sourceB, references: new[] { refA }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (6,12): error CS0570: 'R2.F' is not supported by the language // r2.F = ref r1; @@ -2163,7 +2270,7 @@ static ref T F2(T t) return ref r2.F; } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,5): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // ref T F; @@ -2172,7 +2279,7 @@ static ref T F2(T t) // return ref r2.F; Diagnostic(ErrorCode.ERR_EscapeVariable, "r2.F").WithArguments("r2").WithLocation(13, 20)); - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,20): error CS8352: Cannot use variable 'r2' in this context because it may expose referenced variables outside of their declaration scope // return ref r2.F; @@ -2194,7 +2301,7 @@ static unsafe ref T F2(T t) return ref r2.F; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.UnsafeDebugDll); comp.VerifyEmitDiagnostics( // (9,20): warning CS9077: Use of variable 'r2' in this context may expose referenced variables outside of their declaration scope // return ref r2.F; @@ -2245,7 +2352,7 @@ static R F2(R r2) return r2; } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,5): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // ref T F; @@ -2261,7 +2368,7 @@ static R F2(R r2) Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r2.F = ref t").WithArguments("F", "t").WithLocation(18, 9) ); - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,9): error CS9079: Cannot ref-assign 't' to 'F' because 't' can only escape the current method through a return statement. // F = ref t; @@ -2300,7 +2407,7 @@ ref struct R public ref int Field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"42 43")).VerifyDiagnostics(). @@ -2356,7 +2463,7 @@ ref struct R public ref int Field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (10,9): error CS8374: Cannot ref-assign 'i' to 'Field' because 'i' has a narrower escape scope than 'Field'. // r.Field = ref i; @@ -2392,7 +2499,7 @@ ref struct R public ref int Field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")).VerifyDiagnostics(). VerifyIL("Program.Test", @" @@ -2442,7 +2549,7 @@ ref struct R public ref int Field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")).VerifyDiagnostics(). VerifyIL("Program.Test", @" @@ -3200,55 +3307,55 @@ static T G2A() { T t = default; U u = default; - return F2(t, in u); + return F2(t, in u); } static T G2B() { T t = default; U u = default; - return F2(t, u); + return F2(t, u); } static T G3() { T t = default; U u = default; - return F3(t, out u); + return F3(t, out u); } static T G4() { T t = default; U u = default; - return F4(ref t, ref u); + return F4(ref t, ref u); } static T G5A() { T t = default; U u = default; - return F5(ref t, in u); + return F5(ref t, in u); } static T G5B() { T t = default; U u = default; - return F5(ref t, u); + return F5(ref t, u); } static T G6() { T t = default; U u = default; - return F6(ref t, out u); + return F6(ref t, out u); } static T G7A() { T t = default; U u = default; - return F7(in t, in u); + return F7(in t, in u); } static T G7B() { T t = default; U u = default; - return F7(t, u); + return F7(t, u); } static T G8A() { @@ -3266,7 +3373,7 @@ static T G9() { T t = default; U u = default; - return F9(out t, out u); + return F9(out t, out u); } }"; @@ -3688,7 +3795,7 @@ class Program static ref T F05(ref R x) { R y = default; return ref F5(ref x, ref y); } static ref T F20(scoped ref R x) { R y = default; return ref F0(ref x, ref y); } // 2 - static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 + static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 static ref T F25(scoped ref R x) { R y = default; return ref F5(ref x, ref y); } } ref struct R { } @@ -3707,13 +3814,12 @@ ref struct R { } // (13,82): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method // static ref T F20(scoped ref R x) { R y = default; return ref F0(ref x, ref y); } // 2 Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(13, 82), - // (14,75): error CS8347: Cannot use a result of 'Program.F2(ref R, ref R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope - // static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 - Diagnostic(ErrorCode.ERR_EscapeCall, "F2(ref x, ref y)").WithArguments("Program.F2(ref R, ref R)", "x").WithLocation(14, 75), + // (14,75): error CS8347: Cannot use a result of 'Program.F2(ref R, scoped ref R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F2(ref x, ref y)").WithArguments("Program.F2(ref R, scoped ref R)", "x").WithLocation(14, 75), // (14,82): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method - // static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 - Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(14, 82) - ); + // static ref T F22(scoped ref R x) { R y = default; return ref F2(ref x, ref y); } // 3 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(14, 82)); } [Fact] @@ -3749,15 +3855,15 @@ class Program // (15,68): error CS8347: Cannot use a result of 'Program.F0(R, R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // static R F10(scoped R x, int i) { var y = new R(ref i); return F0(x, y); } // 2 Diagnostic(ErrorCode.ERR_EscapeCall, "F0(x, y)").WithArguments("Program.F0(R, R)", "x").WithLocation(15, 68), - // (15,71): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (15,71): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R F10(scoped R x, int i) { var y = new R(ref i); return F0(x, y); } // 2 - Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("R").WithLocation(15, 71), - // (16,68): error CS8347: Cannot use a result of 'Program.F1(R, R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("scoped R").WithLocation(15, 71), + // (16,68): error CS8347: Cannot use a result of 'Program.F1(R, scoped R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // static R F11(scoped R x, int i) { var y = new R(ref i); return F1(x, y); } // 3 - Diagnostic(ErrorCode.ERR_EscapeCall, "F1(x, y)").WithArguments("Program.F1(R, R)", "x").WithLocation(16, 68), - // (16,71): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + Diagnostic(ErrorCode.ERR_EscapeCall, "F1(x, y)").WithArguments("Program.F1(R, scoped R)", "x").WithLocation(16, 68), + // (16,71): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R F11(scoped R x, int i) { var y = new R(ref i); return F1(x, y); } // 3 - Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("R").WithLocation(16, 71)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("scoped R").WithLocation(16, 71)); } [Fact] @@ -3905,15 +4011,15 @@ static void F(ref R x) // (20,23): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F0(ref x, ref y); // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(20, 23), - // (21,9): error CS8350: This combination of arguments to 'Program.F2(ref R, ref R)' is disallowed because it may expose variables referenced by parameter 'b' outside of their declaration scope + // (21,9): error CS8350: This combination of arguments to 'Program.F2(ref R, scoped ref R)' is disallowed because it may expose variables referenced by parameter 'b' outside of their declaration scope // F2(ref x, ref y); // 2 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, ref y)").WithArguments("Program.F2(ref R, ref R)", "b").WithLocation(21, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, ref y)").WithArguments("Program.F2(ref R, scoped ref R)", "b").WithLocation(21, 9), // (21,23): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F2(ref x, ref y); // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(21, 23), - // (22,9): error CS8350: This combination of arguments to 'Program.F5(ref R, ref R)' is disallowed because it may expose variables referenced by parameter 'b' outside of their declaration scope + // (22,9): error CS8350: This combination of arguments to 'Program.F5(scoped ref R, scoped ref R)' is disallowed because it may expose variables referenced by parameter 'b' outside of their declaration scope // F5(ref x, ref y); // 3 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F5(ref x, ref y)").WithArguments("Program.F5(ref R, ref R)", "b").WithLocation(22, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "F5(ref x, ref y)").WithArguments("Program.F5(scoped ref R, scoped ref R)", "b").WithLocation(22, 9), // (22,23): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F5(ref x, ref y); // 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(22, 23), @@ -3923,15 +4029,15 @@ static void F(ref R x) // (24,16): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F0(ref y, ref x); // 4 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(24, 16), - // (25,9): error CS8350: This combination of arguments to 'Program.F2(ref R, ref R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope + // (25,9): error CS8350: This combination of arguments to 'Program.F2(ref R, scoped ref R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope // F2(ref y, ref x); // 5 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref y, ref x)").WithArguments("Program.F2(ref R, ref R)", "a").WithLocation(25, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref y, ref x)").WithArguments("Program.F2(ref R, scoped ref R)", "a").WithLocation(25, 9), // (25,16): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F2(ref y, ref x); // 5 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(25, 16), - // (26,9): error CS8350: This combination of arguments to 'Program.F5(ref R, ref R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope + // (26,9): error CS8350: This combination of arguments to 'Program.F5(scoped ref R, scoped ref R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope // F5(ref y, ref x); // 6 - Diagnostic(ErrorCode.ERR_CallArgMixing, "F5(ref y, ref x)").WithArguments("Program.F5(ref R, ref R)", "a").WithLocation(26, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "F5(ref y, ref x)").WithArguments("Program.F5(scoped ref R, scoped ref R)", "a").WithLocation(26, 9), // (26,16): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // F5(ref y, ref x); // 6 Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(26, 16)); @@ -4018,7 +4124,7 @@ static void F1(scoped ref R a, __arglist) { } comp.VerifyEmitDiagnostics(); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/64471")] public void MethodArgumentsMustMatch_07_2() { var source = @@ -4052,14 +4158,14 @@ static R F0(__arglist) // The __arglist operator here assumes that `F0` could do `y.RB = ref y`. // These assumptions are contradictory, but it just ends up being more strict in both directions. // Tracking in https://github.com/dotnet/roslyn/issues/64130 - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (17,9): error CS8374: Cannot ref-assign 'r.B' to 'RB' because 'r.B' has a narrower escape scope than 'RB'. // r.RB = ref r.B; // 1 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.RB = ref r.B").WithArguments("RB", "r.B").WithLocation(17, 9)); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/64471")] public void MethodArgumentsMustMatch_07_3() { // demonstrate the non-ref-fields behavior. @@ -4082,7 +4188,7 @@ static ref int F0(__arglist) static ref int F3(scoped ref int y) { return ref F0(__arglist(ref y)); } static ref int F4(ref int y) { return ref F0(__arglist(ref y)); } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (10,20): error CS8157: Cannot return 'r' by reference because it was initialized to a value that cannot be returned by reference // return ref r; // 1 @@ -4523,21 +4629,21 @@ static ref readonly int F3() var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (11,20): error CS8347: Cannot use a result of 'Program.F(in int, in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (11,20): error CS8347: Cannot use a result of 'Program.F(in int, scoped in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // return ref F(in x1, in y1); // 1 - Diagnostic(ErrorCode.ERR_EscapeCall, "F(in x1, in y1)").WithArguments("Program.F(in int, in int)", "x").WithLocation(11, 20), + Diagnostic(ErrorCode.ERR_EscapeCall, "F(in x1, in y1)").WithArguments("Program.F(in int, scoped in int)", "x").WithLocation(11, 20), // (11,25): error CS8168: Cannot return local 'x1' by reference because it is not a ref local // return ref F(in x1, in y1); // 1 Diagnostic(ErrorCode.ERR_RefReturnLocal, "x1").WithArguments("x1").WithLocation(11, 25), - // (17,20): error CS8347: Cannot use a result of 'Program.F(in int, in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (17,20): error CS8347: Cannot use a result of 'Program.F(in int, scoped in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // return ref F(x2, in y2); // 2 - Diagnostic(ErrorCode.ERR_EscapeCall, "F(x2, in y2)").WithArguments("Program.F(in int, in int)", "x").WithLocation(17, 20), + Diagnostic(ErrorCode.ERR_EscapeCall, "F(x2, in y2)").WithArguments("Program.F(in int, scoped in int)", "x").WithLocation(17, 20), // (17,22): error CS8168: Cannot return local 'x2' by reference because it is not a ref local // return ref F(x2, in y2); // 2 Diagnostic(ErrorCode.ERR_RefReturnLocal, "x2").WithArguments("x2").WithLocation(17, 22), - // (22,20): error CS8347: Cannot use a result of 'Program.F(in int, in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (22,20): error CS8347: Cannot use a result of 'Program.F(in int, scoped in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // return ref F(3, in y3); // 3 - Diagnostic(ErrorCode.ERR_EscapeCall, "F(3, in y3)").WithArguments("Program.F(in int, in int)", "x").WithLocation(22, 20), + Diagnostic(ErrorCode.ERR_EscapeCall, "F(3, in y3)").WithArguments("Program.F(in int, scoped in int)", "x").WithLocation(22, 20), // (22,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return ref F(3, in y3); // 3 Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "3").WithLocation(22, 22)); @@ -4612,7 +4718,7 @@ class Program { static ref T F2(ref R r) => ref r.F0(); }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -4663,7 +4769,7 @@ static void M4(T t4) s = new S { F = t4 }; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (12,16): error CS8347: Cannot use a result of 'S.S(ref T)' in this context because it may expose variables referenced by parameter 't' outside of their declaration scope // this = new S(ref t0); @@ -4738,7 +4844,7 @@ static void M4(T t4) s = new S { F = t4 }; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.UnsafeDebugDll); comp.VerifyEmitDiagnostics( // (13,29): warning CS9084: This returns a parameter by reference 't0' but it is not a ref parameter // this = new S(ref t0); @@ -4794,7 +4900,7 @@ static void F(ref T t) new S4().F = t; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (27,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // new S1().F = ref t; @@ -4824,7 +4930,7 @@ public S(ref T t) F = t; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -4863,7 +4969,7 @@ static void M(ref S x) y.F4 = ref t; } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (10,9): error CS8374: Cannot ref-assign 't' to 'F1' because 't' has a narrower escape scope than 'F1'. // F1 = ref t; @@ -4931,7 +5037,7 @@ object P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -4963,29 +5069,29 @@ object P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (7,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + // (7,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tValue; // 1 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), - // (8,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(7, 9), + // (8,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tRef; // 2 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), - // (9,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(8, 9), + // (9,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tOut; // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 9), - // (10,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(9, 9), + // (10,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tIn; // 4 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(10, 9), - // (16,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(10, 9), + // (16,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetValue(); // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(16, 13), - // (17,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(16, 13), + // (17,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetRef(); // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(17, 13), - // (18,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(17, 13), + // (18,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetRefReadonly(); // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(18, 13)); } [Fact] @@ -5016,7 +5122,7 @@ object P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -5048,29 +5154,29 @@ object P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (7,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + // (7,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tValue; // 1 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), - // (8,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(7, 9), + // (8,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tRef; // 2 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), - // (9,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(8, 9), + // (9,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tOut; // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 9), - // (10,9): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(9, 9), + // (10,9): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = tIn; // 4 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(10, 9), - // (16,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(10, 9), + // (16,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetValue(); // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(16, 13), - // (17,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(16, 13), + // (17,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetRef(); // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(17, 13), - // (18,13): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(17, 13), + // (18,13): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // F = GetRefReadonly(); // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "F").WithLocation(18, 13)); } [Fact] @@ -5100,7 +5206,7 @@ T P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,9): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // F = ref tValue; // 1 @@ -5108,15 +5214,15 @@ T P // (9,9): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // F = ref tOut; // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "F = ref tOut").WithArguments("F", "tOut").WithLocation(9, 9), - // (10,17): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable + // (10,17): error CS8331: Cannot assign to variable 'tIn' or use it as the right hand side of a ref assignment because it is a readonly variable // F = ref tIn; // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "tIn").WithLocation(10, 17), // (16,13): error CS8374: Cannot ref-assign 'value' to 'F' because 'value' has a narrower escape scope than 'F'. // F = ref value; // 4 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "F = ref value").WithArguments("F", "value").WithLocation(16, 13), - // (18,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' or use it as the right hand side of a ref assignment because it is a readonly variable + // (18,21): error CS8331: Cannot assign to method 'GetRefReadonly' or use it as the right hand side of a ref assignment because it is a readonly variable // F = ref GetRefReadonly(); // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(18, 21)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "GetRefReadonly").WithLocation(18, 21)); } [Fact] @@ -5146,7 +5252,7 @@ T P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,9): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // F = ref tValue; // 1 @@ -5186,7 +5292,7 @@ T P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,9): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // F = ref tValue; // 1 @@ -5194,15 +5300,15 @@ T P // (9,9): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // F = ref tOut; // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "F = ref tOut").WithArguments("F", "tOut").WithLocation(9, 9), - // (10,17): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable + // (10,17): error CS8331: Cannot assign to variable 'tIn' or use it as the right hand side of a ref assignment because it is a readonly variable // F = ref tIn; // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "tIn").WithLocation(10, 17), // (16,13): error CS8374: Cannot ref-assign 'value' to 'F' because 'value' has a narrower escape scope than 'F'. // F = ref value; // 4 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "F = ref value").WithArguments("F", "value").WithLocation(16, 13), - // (18,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' or use it as the right hand side of a ref assignment because it is a readonly variable + // (18,21): error CS8331: Cannot assign to method 'GetRefReadonly' or use it as the right hand side of a ref assignment because it is a readonly variable // F = ref GetRefReadonly(); // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(18, 21)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "GetRefReadonly").WithLocation(18, 21)); } [Fact] @@ -5232,7 +5338,7 @@ T P static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,9): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // F = ref tValue; // 1 @@ -5353,7 +5459,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -5598,56 +5704,56 @@ class Program static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (9,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + // (9,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 59), - // (10,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(9, 59), + // (10,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 59), - // (11,75): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(10, 59), + // (11,75): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 75), - // (12,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(11, 75), + // (12,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 59), - // (14,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(12, 59), + // (14,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(14, 64), - // (15,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(14, 64), + // (15,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(15, 64), - // (16,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(15, 64), + // (16,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(16, 80), - // (17,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(16, 80), + // (17,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(17, 64), - // (19,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(17, 64), + // (19,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(19, 80), - // (20,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(19, 80), + // (20,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(20, 80), - // (21,96): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(20, 80), + // (21,96): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(21, 96), - // (22,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(21, 96), + // (22,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(22, 80), - // (24,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(22, 80), + // (24,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(24, 61), - // (25,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(24, 61), + // (25,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(25, 61), - // (26,77): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(25, 61), + // (26,77): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(26, 77), - // (27,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(26, 77), + // (27,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(27, 61)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(27, 61)); } [Fact] @@ -5758,7 +5864,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -6003,56 +6109,56 @@ class Program static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (9,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + // (9,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 59), - // (10,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(9, 59), + // (10,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 59), - // (11,75): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(10, 59), + // (11,75): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 75), - // (12,59): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(11, 75), + // (12,59): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 59), - // (14,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(12, 59), + // (14,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(14, 64), - // (15,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(14, 64), + // (15,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(15, 64), - // (16,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(15, 64), + // (16,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(16, 80), - // (17,64): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(16, 80), + // (17,64): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(17, 64), - // (19,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "F").WithLocation(17, 64), + // (19,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(19, 80), - // (20,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(19, 80), + // (20,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(20, 80), - // (21,96): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(20, 80), + // (21,96): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(21, 96), - // (22,80): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(21, 96), + // (22,80): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(22, 80), - // (24,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "F").WithLocation(22, 80), + // (24,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(24, 61), - // (25,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(24, 61), + // (25,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(25, 61), - // (26,77): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(25, 61), + // (26,77): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(26, 77), - // (27,61): error CS8331: Cannot assign to field 'S.F' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(26, 77), + // (27,61): error CS8331: Cannot assign to field 'F' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(27, 61)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "F").WithLocation(27, 61)); } [Fact] @@ -6079,15 +6185,15 @@ class Program static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } - static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 - static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 12 static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 13 static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 14 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 15 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,59): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 @@ -6098,9 +6204,9 @@ class Program // (11,75): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s.F = ref tOut").WithArguments("F", "tOut").WithLocation(11, 75), - // (12,69): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable + // (12,69): error CS8331: Cannot assign to variable 'tIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(12, 69), + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "tIn").WithLocation(12, 69), // (14,64): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tValue").WithArguments("F", "tValue").WithLocation(14, 64), @@ -6110,31 +6216,30 @@ class Program // (16,80): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tOut").WithArguments("F", "tOut").WithLocation(16, 80), - // (17,77): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable + // (17,77): error CS8331: Cannot assign to variable 'tIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(17, 77), + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "tIn").WithLocation(17, 77), // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), // (21,96): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. - // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 10 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tOut").WithArguments("F", "tOut").WithLocation(21, 96), - // (22,93): error CS8331: Cannot assign to variable 'in T' or use it as the right hand side of a ref assignment because it is a readonly variable - // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(22, 93), - // (24,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + // (22,93): error CS8331: Cannot assign to variable 'tIn' or use it as the right hand side of a ref assignment because it is a readonly variable + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 11 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "tIn").WithLocation(22, 93), + // (24,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 12 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), - // (25,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(24, 61), + // (25,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 13 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), - // (26,77): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(25, 61), + // (26,77): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 14 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), - // (27,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(26, 77), + // (27,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 15 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61) - ); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(27, 61)); // Valid cases from above. source = @@ -6161,7 +6266,7 @@ static void Main() Console.WriteLine(s.F); } }"; - comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"6")); @@ -6211,7 +6316,7 @@ class Program static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 9 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 10 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,59): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 @@ -6243,18 +6348,18 @@ class Program // (21,96): error CS8374: Cannot ref-assign 'tOut' to 'F' because 'tOut' has a narrower escape scope than 'F'. // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 6 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tOut").WithArguments("F", "tOut").WithLocation(21, 96), - // (24,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + // (24,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), - // (25,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(24, 61), + // (25,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 8 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), - // (26,77): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(25, 61), + // (26,77): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 9 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), - // (27,61): error CS8332: Cannot assign to a member of variable 'in S' or use it as the right hand side of a ref assignment because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(26, 77), + // (27,61): error CS8332: Cannot assign to a member of variable 'sIn' or use it as the right hand side of a ref assignment because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 10 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "sIn").WithLocation(27, 61)); // Valid cases from above. source = @@ -6286,7 +6391,7 @@ static void Main() Console.WriteLine(s.F); } }"; - comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"6 6")); @@ -6347,7 +6452,7 @@ class Program static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 @@ -6431,7 +6536,7 @@ class Program static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 @@ -6518,7 +6623,7 @@ class Program static void FromInReadonlyRef(in S s, out T t) { t = s.ReadonlyRef; } static void FromInReadonlyRefReadonly(in S s, out T t) { t = s.ReadonlyRefReadonly; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6556,32 +6661,32 @@ class Program static void FromInReadonlyRef(in S s) { ref T t = ref s.ReadonlyRef; } static void FromInReadonlyRefReadonly(in S s) { ref T t = ref s.ReadonlyRefReadonly; } // 8 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (12,73): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + // (12,73): error CS8329: Cannot use field 'RefReadonly' as a ref or out value because it is a readonly variable // static void FromValueRefReadonly(S s) { ref T t = ref s.RefReadonly; } // 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(12, 73), - // (14,73): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "RefReadonly").WithLocation(12, 73), + // (14,73): error CS8329: Cannot use field 'ReadonlyRefReadonly' as a ref or out value because it is a readonly variable // static void FromValueReadonlyRefReadonly(S s) { ref T t = ref s.ReadonlyRefReadonly; } // 2 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(14, 73), - // (17,75): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "ReadonlyRefReadonly").WithLocation(14, 73), + // (17,75): error CS8329: Cannot use field 'RefReadonly' as a ref or out value because it is a readonly variable // static void FromRefRefReadonly(ref S s) { ref T t = ref s.RefReadonly; } // 3 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(17, 75), - // (19,75): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "RefReadonly").WithLocation(17, 75), + // (19,75): error CS8329: Cannot use field 'ReadonlyRefReadonly' as a ref or out value because it is a readonly variable // static void FromRefReadonlyRefReadonly(ref S s) { ref T t = ref s.ReadonlyRefReadonly; } // 4 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(19, 75), - // (22,88): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "ReadonlyRefReadonly").WithLocation(19, 75), + // (22,88): error CS8329: Cannot use field 'RefReadonly' as a ref or out value because it is a readonly variable // static void FromOutRefReadonly(out S s) { s = default; ref T t = ref s.RefReadonly; } // 5 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(22, 88), - // (24,88): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "RefReadonly").WithLocation(22, 88), + // (24,88): error CS8329: Cannot use field 'ReadonlyRefReadonly' as a ref or out value because it is a readonly variable // static void FromOutReadonlyRefReadonly(out S s) { s = default; ref T t = ref s.ReadonlyRefReadonly; } // 6 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(24, 88), - // (27,73): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "ReadonlyRefReadonly").WithLocation(24, 88), + // (27,73): error CS8329: Cannot use field 'RefReadonly' as a ref or out value because it is a readonly variable // static void FromInRefReadonly(in S s) { ref T t = ref s.RefReadonly; } // 7 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(27, 73), - // (29,73): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "RefReadonly").WithLocation(27, 73), + // (29,73): error CS8329: Cannot use field 'ReadonlyRefReadonly' as a ref or out value because it is a readonly variable // static void FromInReadonlyRefReadonly(in S s) { ref T t = ref s.ReadonlyRefReadonly; } // 8 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(29, 73)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "ReadonlyRefReadonly").WithLocation(29, 73)); } [Fact] @@ -6618,7 +6723,7 @@ class Program static void FromInReadonlyRef(in S s) { ref readonly T t = ref s.ReadonlyRef; } static void FromInReadonlyRefReadonly(in S s) { ref readonly T t = ref s.ReadonlyRefReadonly; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6645,9 +6750,9 @@ public void RefReturn() // (5,59): error CS9075: Cannot return a parameter by reference 't' because it is scoped to the current method // static ref T F3(out T t) { t = default; return ref t; } // 2 Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "t").WithArguments("t").WithLocation(5, 59), - // (6,39): error CS8333: Cannot return variable 'in T' by writable reference because it is a readonly variable + // (6,39): error CS8333: Cannot return variable 't' by writable reference because it is a readonly variable // static ref T F4(in T t) => ref t; // 3 - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "t").WithArguments("variable", "in T").WithLocation(6, 39), + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "t").WithArguments("variable", "t").WithLocation(6, 39), // (7,45): error CS8166: Cannot return a parameter by reference 't' because it is not a ref parameter // static ref readonly T F5(T t) => ref t; // 4 Diagnostic(ErrorCode.ERR_RefReturnParameter, "t").WithArguments("t").WithLocation(7, 45), @@ -6677,7 +6782,7 @@ class Program static ref readonly T F9(out S s) { s = default; return ref s.F; } static ref readonly T F10(in S s) => ref s.F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6702,23 +6807,23 @@ class Program static ref readonly T F9(out S s) { s = default; return ref s.F; } static ref readonly T F10(in S s) => ref s.F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (4,30): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // (4,30): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // public ref T F1() => ref F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(4, 30), - // (9,39): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "F").WithLocation(4, 30), + // (9,39): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F3(S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 39), - // (10,43): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(9, 39), + // (10,43): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F4(ref S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 43), - // (11,62): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(10, 43), + // (11,62): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F5(out S s) { s = default; return ref s.F; } - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 62), - // (12,42): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(11, 62), + // (12,42): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F6(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 42)); + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(12, 42)); } [Fact] @@ -6742,7 +6847,7 @@ class Program static ref readonly T F9(out S s) { s = default; return ref s.F; } static ref readonly T F10(in S s) => ref s.F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6767,23 +6872,23 @@ class Program static ref readonly T F9(out S s) { s = default; return ref s.F; } static ref readonly T F10(in S s) => ref s.F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (4,30): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // (4,30): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // public ref T F1() => ref F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(4, 30), - // (9,39): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "F").WithLocation(4, 30), + // (9,39): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F3(S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 39), - // (10,43): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(9, 39), + // (10,43): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F4(ref S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 43), - // (11,62): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(10, 43), + // (11,62): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F5(out S s) { s = default; return ref s.F; } - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 62), - // (12,42): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(11, 62), + // (12,42): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // static ref T F6(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 42)); + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(12, 42)); } [Fact] @@ -6826,7 +6931,7 @@ static void M2(ref T t) { } static void M3(out T t) { t = default; } static void M4(in T t) { } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6870,26 +6975,26 @@ static void M2(ref T t) { } static void M3(out T t) { t = default; } static void M4(in T t) { } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // (8,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), - // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(8, 16), + // (9,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 2 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), - // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(9, 16), + // (18,20): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 3 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), - // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(18, 20), + // (19,20): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 4 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), - // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(19, 20), + // (27,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 5 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), - // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(27, 16), + // (28,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 6 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(28, 16)); } [Fact] @@ -6932,7 +7037,7 @@ static void M2(ref T t) { } static void M3(out T t) { t = default; } static void M4(in T t) { } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -6976,26 +7081,26 @@ static void M2(ref T t) { } static void M3(out T t) { t = default; } static void M4(in T t) { } }"; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // (8,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), - // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(8, 16), + // (9,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 2 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), - // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(9, 16), + // (18,20): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 3 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), - // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(18, 20), + // (19,20): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 4 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), - // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(19, 20), + // (27,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M2(ref F); // 5 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), - // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(27, 16), + // (28,16): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // M3(out F); // 6 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "F").WithLocation(28, 16)); } [Fact] @@ -7038,7 +7143,7 @@ static void M4(in T t) { } static void FromIn4A(in S s) { M4(in s.F); } static void FromIn4B(in S s) { M4(s.F); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -7082,32 +7187,32 @@ static void M4(in T t) { } static void FromIn4A(in S s) { M4(in s.F); } static void FromIn4B(in S s) { M4(s.F); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (14,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // (14,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromValue2(S s) { M2(ref s.F); } // 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(14, 49), - // (15,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(14, 49), + // (15,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromValue3(S s) { M3(out s.F); } // 2 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(15, 49), - // (20,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(15, 49), + // (20,51): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromRef2(ref S s) { M2(ref s.F); } // 3 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(20, 51), - // (21,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(20, 51), + // (21,51): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromRef3(ref S s) { M3(out s.F); } // 4 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(21, 51), - // (26,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(21, 51), + // (26,64): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(26, 64), - // (27,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(26, 64), + // (27,64): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(27, 64), - // (32,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(27, 64), + // (32,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromIn2(in S s) { M2(ref s.F); } // 7 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(32, 49), - // (33,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(32, 49), + // (33,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromIn3(in S s) { M3(out s.F); } // 8 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(33, 49)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(33, 49)); } [Fact] @@ -7150,7 +7255,7 @@ static void M4(in T t) { } static void FromIn4A(in S s) { M4(in s.F); } static void FromIn4B(in S s) { M4(s.F); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -7194,32 +7299,32 @@ static void M4(in T t) { } static void FromIn4A(in S s) { M4(in s.F); } static void FromIn4B(in S s) { M4(s.F); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (14,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // (14,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromValue2(S s) { M2(ref s.F); } // 1 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(14, 49), - // (15,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(14, 49), + // (15,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromValue3(S s) { M3(out s.F); } // 2 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(15, 49), - // (20,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(15, 49), + // (20,51): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromRef2(ref S s) { M2(ref s.F); } // 3 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(20, 51), - // (21,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(20, 51), + // (21,51): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromRef3(ref S s) { M3(out s.F); } // 4 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(21, 51), - // (26,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(21, 51), + // (26,64): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(26, 64), - // (27,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(26, 64), + // (27,64): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(27, 64), - // (32,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(27, 64), + // (32,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromIn2(in S s) { M2(ref s.F); } // 7 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(32, 49), - // (33,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(32, 49), + // (33,49): error CS8329: Cannot use field 'F' as a ref or out value because it is a readonly variable // static void FromIn3(in S s) { M3(out s.F); } // 8 - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(33, 49)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "F").WithLocation(33, 49)); } [Fact] @@ -7253,7 +7358,7 @@ static void M4(in T t) { } static void FromIn4A(in S s) { M4(in s.F); } static void FromIn4B(in S s) { M4(s.F); } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithFeature("peverify-compat"), runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithFeature("peverify-compat"), targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } @@ -7287,7 +7392,7 @@ static void NewField(ref R r) r.S = new S(); } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"42 42 @@ -7333,7 +7438,7 @@ static void Write(S s, int value) s.F = value; } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"42 42 @@ -7405,7 +7510,7 @@ static T ReadIn(in R2 r2In) return r2In.R1.F; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (10,12): error CS9050: A ref field cannot refer to a ref struct. // public ref R1 R1; @@ -7433,7 +7538,7 @@ static T ReadWrite2(ref T t) return new S(1).F = ref t; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,16): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // return new S().F = ref t; @@ -7459,19 +7564,21 @@ class Program static void Main() { int i = 1; - Try(() => ReadAndDiscard1(ref i)); - Try(() => ReadAndDiscardNoArg()); - Try(() => ReadAndDiscard2(new S(ref i))); + Try(1, () => ReadAndDiscard1(ref i)); + Try(2, () => ReadAndDiscardNoArg()); + Try(3, () => ReadAndDiscard2(new S(ref i))); + Try(4, () => ReadAndDiscard2(new S())); - void Try(Action a) + void Try(int i, Action a) { try { a(); + Console.WriteLine(i); } catch (NullReferenceException) { - System.Console.WriteLine(""NullReferenceException""); + Console.WriteLine(""NullReferenceException""); } } } @@ -7488,8 +7595,13 @@ static void ReadAndDiscard2(in S s) _ = s.F; } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); - var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("NullReferenceException")); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"1 +NullReferenceException +3 +NullReferenceException +")); verifier.VerifyIL("Program.ReadAndDiscard1", """ { // Code size 18 (0x12) @@ -7552,7 +7664,7 @@ static void Main() static ref T RefReturn(S s) => ref s.F; static ref readonly T RefReadonlyReturn(S s) => ref s.F; }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); var expectedIL = @"{ @@ -7589,7 +7701,7 @@ static void Main() static ref T RefReturn(ref S s) => ref s.F; static ref readonly T RefReadonlyReturn(ref S s) => ref s.F; }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); var expectedIL = @"{ @@ -7626,7 +7738,7 @@ static void Main() static ref T RefReturn(in S s) => ref s.F; static ref readonly T RefReadonlyReturn(in S s) => ref s.F; }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); var expectedIL = @"{ @@ -7671,7 +7783,7 @@ static ref readonly T RefReadonlyReturn(out S s, ref T t) return ref s.F; } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); var expectedIL = @"{ @@ -7721,7 +7833,7 @@ static void Subtract(S s, int offset) s.F -= offset; } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"43 43 @@ -7790,7 +7902,7 @@ static void Main() Console.WriteLine(x); }} }}"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"43 43 @@ -7861,7 +7973,7 @@ static ref T ConditionalOperatorRef(bool b, ref S sx, ref S sy) return ref b ? ref sx.F : ref sy.F; } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"1 2 @@ -7924,7 +8036,7 @@ static string ConditionalAccess(S s) return s.F?.ToString(); } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"1 2 @@ -7998,10 +8110,7 @@ static void Deconstruct(Pair pair, S s1, S s2) } }"; var references = TargetFrameworkUtil.GetReferences(TargetFramework.Standard, additionalReferences: null); - // Note: we use skipExtraValidation so that nobody pulls - // on the compilation or its references before we set the RuntimeSupportsByRefFields flag. - var comp = CreateEmptyCompilation(source, references: CopyWithoutSharingCachedSymbols(references), options: TestOptions.ReleaseExe, skipExtraValidation: true); - comp.Assembly.RuntimeSupportsByRefFields = true; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"(1, Hello world)")); verifier.VerifyIL("Program.Deconstruct", @"{ @@ -8390,12 +8499,12 @@ static R2 F4(ref int i) else { comp.VerifyEmitDiagnostics( - // (16,16): error CS8352: Cannot use variable 'y1' in this context because it may expose referenced variables outside of their declaration scope - // return y1; // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "y1").WithArguments("y1").WithLocation(16, 16), - // (23,16): error CS8352: Cannot use variable 'y2' in this context because it may expose referenced variables outside of their declaration scope - // return y2; // 2 - Diagnostic(ErrorCode.ERR_EscapeVariable, "y2").WithArguments("y2").WithLocation(23, 16)); + // (16,16): error CS8352: Cannot use variable 'y1' in this context because it may expose referenced variables outside of their declaration scope + // return y1; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "y1").WithArguments("y1").WithLocation(16, 16), + // (23,16): error CS8352: Cannot use variable 'y2' in this context because it may expose referenced variables outside of their declaration scope + // return y2; // 2 + Diagnostic(ErrorCode.ERR_EscapeVariable, "y2").WithArguments("y2").WithLocation(23, 16)); } } @@ -8612,7 +8721,7 @@ static void M(in T x, in T y) Console.WriteLine(y); } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"1 2 @@ -8709,7 +8818,7 @@ static void F(ref R r) r.Next = ref r; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); // https://github.com/dotnet/roslyn/issues/62098: Allow ref field of the containing type. comp.VerifyEmitDiagnostics( // (5,12): error CS9050: A ref field cannot refer to a ref struct. @@ -8868,6 +8977,67 @@ static void Main() Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.P2").WithLocation(22, 9)); } + [Fact] + [WorkItem(60807, "https://github.com/dotnet/roslyn/issues/60807")] + public void RefAndOut_PropertiesAndIndexers_As_ValuesAndParameters() + { + var source = +@" +var c = new C(); +var r = new R(); +//expressions +ref int n = ref c.N; //CS0206 +ref var l = ref c[0]; //CS0206 +ref var l2 = ref r[0];//OK +_ = M(ref c.N); //CS0206 +_ = M(ref r.N); //OK +_ = M(ref c[0]); //CS0206 +_ = M(ref r[0]); //OK +_ = M2(out c.N); //CS0206 +_ = M2(out r.N); //OK +_ = M2(out c[0]); //CS0206 +_ = M2(out r[0]); //OK +//definitions +static string M(ref int number) { return """"; } +static string M2(out int number) { number = 42; return """"; } +class C +{ + public int N { get; set; } + private int[] arr = new int[100]; + public int this[int i] => arr[i]; +} +ref struct R +{ + public R(){} + private ref int n; + public ref int N => ref n; + private ref int[] arr = new int[1]; + public ref int this[int i] => ref arr[i]; +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // ref int n = ref c.N; //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c.N").WithLocation(5, 17), + // (6,17): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // ref var l = ref c[0]; //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c[0]").WithLocation(6, 17), + // (8,11): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // _ = M(ref c.N); //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c.N").WithLocation(8, 11), + // (10,11): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // _ = M(ref c[0]); //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c[0]").WithLocation(10, 11), + // (12,12): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // _ = M2(out c.N); //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c.N").WithLocation(12, 12), + // (14,12): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // _ = M2(out c[0]); //CS0206 + Diagnostic(ErrorCode.ERR_RefProperty, "c[0]").WithLocation(14, 12) + ); + } + [Fact] public void RefAccessor_Value() { @@ -8925,7 +9095,7 @@ static void Main() Console.WriteLine(s.t); } }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -9029,7 +9199,7 @@ class C { public static void M() where T : unmanaged { } }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (4,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('StructWithRefField') // StructWithRefField* p = stackalloc StructWithRefField[10]; // 1, 2 @@ -9068,7 +9238,7 @@ class C { public static void M() where T : unmanaged { } }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (4,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('StructWithIndirectRefField') // StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 @@ -9079,10 +9249,10 @@ public static void M() where T : unmanaged { } // (5,7): error CS0306: The type 'StructWithIndirectRefField' may not be used as a type argument // C.M(); // 3 Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("StructWithIndirectRefField").WithLocation(5, 7), - // (10,36): warning CS0649: Field 'StructWithIndirectRefField.Field' is never assigned to, and will always have its default value + // (10,36): warning CS0649: Field 'StructWithIndirectRefField.Field' is never assigned to, and will always have its default value // public StructWithRefField Field; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "Field").WithArguments("StructWithIndirectRefField.Field", "").WithLocation(10, 36), - // (14,18): warning CS0649: Field 'StructWithRefField.RefField' is never assigned to, and will always have its default value + // (14,18): warning CS0649: Field 'StructWithRefField.RefField' is never assigned to, and will always have its default value // public ref T RefField; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "RefField").WithArguments("StructWithRefField.RefField", "").WithLocation(14, 18) ); @@ -9108,7 +9278,7 @@ public ref struct StructWithRefField { public ref byte RefField; }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (4,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('StructWithIndirectRefField') // StructWithIndirectRefField* p = stackalloc StructWithIndirectRefField[10]; // 1, 2 @@ -9121,6 +9291,296 @@ public ref struct StructWithRefField Assert.True(comp.GetTypeByMetadataName("StructWithIndirectRefField").IsManagedTypeNoUseSiteDiagnostics); } + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Theory] + [CombinatorialData] + public void RefAssignValueScopeMismatch_01A( + [CombinatorialValues(LanguageVersion.CSharp10, LanguageVersion.CSharp11)] LanguageVersion languageVersion, + bool useUnsafe) + { + string unsafeModifier = useUnsafe ? "unsafe" : ""; + var source = +$@"using System; +class Program +{{ + static void Main() + {{ + Span s = new[] {{ 1 }}; + M(ref s); + }} + {unsafeModifier} static void M(ref Span s) + {{ + Span local = stackalloc int[] {{ 1 }}; + ref Span rL = ref local; + rL = ref s; // 1 + rL = local; + }} +}}"; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), + options: (useUnsafe ? TestOptions.UnsafeReleaseExe : null)); + if (useUnsafe) + { + comp.VerifyEmitDiagnostics( + // (13,9): warning CS9097: This ref-assigns 's' to 'rL' but 's' has a wider value escape scope than 'rL' allowing assignment through 'rL' of values with narrower escapes scopes than 's'. + // rL = ref s; // 1 + Diagnostic(ErrorCode.WRN_RefAssignValEscapeWider, "rL = ref s").WithArguments("rL", "s").WithLocation(13, 9)); + } + else + { + comp.VerifyEmitDiagnostics( + // (13,9): error CS9096: Cannot ref-assign 's' to 'rL' because 's' has a wider value escape scope than 'rL' allowing assignment through 'rL' of values with narrower escapes scopes than 's'. + // rL = ref s; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "rL = ref s").WithArguments("rL", "s").WithLocation(13, 9)); + } + } + + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Theory] + [CombinatorialData] + public void RefAssignValueScopeMismatch_01B( + [CombinatorialValues(LanguageVersion.CSharp10, LanguageVersion.CSharp11)] LanguageVersion languageVersion, + bool useUnsafe) + { + string unsafeModifier = useUnsafe ? "unsafe" : ""; + var source = +$@"using System; +class Program +{{ + static void Main() + {{ + Span s = new[] {{ 1 }}; + M(ref s); + }} + {unsafeModifier} static void M(ref Span s) + {{ + Span local = stackalloc int[] {{ 1 }}; + ref readonly Span rL = ref local; + rL = ref s; // 1 + rL = local; // 2 + }} +}}"; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), + options: (useUnsafe ? TestOptions.UnsafeReleaseExe : null)); + if (useUnsafe) + { + comp.VerifyEmitDiagnostics( + // (13,9): warning CS9097: This ref-assigns 's' to 'rL' but 's' has a wider value escape scope than 'rL' allowing assignment through 'rL' of values with narrower escapes scopes than 's'. + // rL = ref s; // 1 + Diagnostic(ErrorCode.WRN_RefAssignValEscapeWider, "rL = ref s").WithArguments("rL", "s").WithLocation(13, 9), + // (14,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // rL = local; // 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "rL").WithLocation(14, 9)); + } + else + { + comp.VerifyEmitDiagnostics( + // (13,9): error CS9096: Cannot ref-assign 's' to 'rL' because 's' has a wider value escape scope than 'rL' allowing assignment through 'rL' of values with narrower escapes scopes than 's'. + // rL = ref s; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "rL = ref s").WithArguments("rL", "s").WithLocation(13, 9), + // (14,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // rL = local; // 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "rL").WithLocation(14, 9)); + } + } + + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Theory] + [CombinatorialData] + public void RefAssignValueScopeMismatch_02( + [CombinatorialValues(LanguageVersion.CSharp10, LanguageVersion.CSharp11)] LanguageVersion languageVersion, + bool useUnsafe) + { + string unsafeModifier = useUnsafe ? "unsafe" : ""; + var source = +$@"ref struct R +{{ + public R(ref int i) {{ }} +}} +class Program +{{ + static void Main() + {{ + R r = default; + M(ref r); + }} + {unsafeModifier} static void M(ref R r1) + {{ + int i = 0; + R local = new R(ref i); + ref R r2 = ref local; + r2 = ref r1; // 1 + r2 = local; + }} +}}"; + var comp = CreateCompilation(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), + options: (useUnsafe ? TestOptions.UnsafeReleaseExe : null)); + if (languageVersion == LanguageVersion.CSharp10) + { + comp.VerifyEmitDiagnostics(); + } + else if (useUnsafe) + { + comp.VerifyEmitDiagnostics( + // (17,9): warning CS9097: This ref-assigns 'r1' to 'r2' but 'r1' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escapes scopes than 'r1'. + // r2 = ref r1; // 1 + Diagnostic(ErrorCode.WRN_RefAssignValEscapeWider, "r2 = ref r1").WithArguments("r2", "r1").WithLocation(17, 9)); + } + else + { + comp.VerifyEmitDiagnostics( + // (17,9): error CS9096: Cannot ref-assign 'r1' to 'r2' because 'r1' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escapes scopes than 'r1'. + // r2 = ref r1; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "r2 = ref r1").WithArguments("r2", "r1").WithLocation(17, 9)); + } + } + + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void RefAssignValueScopeMismatch_03(LanguageVersion languageVersion) + { + var source = +@"ref struct R1 +{ + public R2 F; +} +ref struct R2 +{ + public R2(ref int i) { } +} +class Program +{ + static void Main() + { + R1 r = default; + M(ref r); + } + static void M(ref R1 r1) + { + int i = 0; + R2 local = new R2(ref i); + ref R2 r2 = ref local; + r2 = ref r1.F; // 1 + r2 = local; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); + if (languageVersion == LanguageVersion.CSharp10) + { + comp.VerifyEmitDiagnostics(); + } + else + { + comp.VerifyEmitDiagnostics( + // (21,9): error CS9096: Cannot ref-assign 'r1.F' to 'r2' because 'r1.F' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escapes scopes than 'r1.F'. + // r2 = ref r1.F; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "r2 = ref r1.F").WithArguments("r2", "r1.F").WithLocation(21, 9)); + } + } + + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Fact] + public void RefAssignValueScopeMismatch_04() + { + var source = +@"using System; +class Program +{ + static Span F() + { + Span s1 = default; + { + scoped ref Span r1 = ref s1; + Span s2 = stackalloc int[1]; + ref Span r2 = ref s2; + r2 = ref r1; // 1 + r2 = s2; + } + return s1; + } +}"; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + comp.VerifyEmitDiagnostics( + // (11,13): error CS9096: Cannot ref-assign 'r1' to 'r2' because 'r1' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escapes scopes than 'r1'. + // r2 = ref r1; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "r2 = ref r1").WithArguments("r2", "r1").WithLocation(11, 13)); + } + + [WorkItem(62618, "https://github.com/dotnet/roslyn/issues/62618")] + [Fact] + public void RefAssignValueScopeMismatch_05() + { + var source = +@"ref struct S +{ + public S(ref int i) { } +} +class Program +{ + static S F() + { + S s1 = default; + scoped ref S r1 = ref s1; + int i = 0; + S s2 = new S(ref i); + ref S r2 = ref s2; + r2 = ref r1; // 1 + r2 = s2; + return s1; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (14,9): error CS9096: Cannot ref-assign 'r1' to 'r2' because 'r1' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escapes scopes than 'r1'. + // r2 = ref r1; // 1 + Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "r2 = ref r1").WithArguments("r2", "r1").WithLocation(14, 9)); + } + + [Theory] + [CombinatorialData] + public void RefAssignValueScopeMismatch_06(bool useUnsafe) + { + string unsafeModifier = useUnsafe ? "unsafe" : ""; + var source = +$@"ref struct S +{{ + public S(ref int i) {{ }} +}} +class Program +{{ + {unsafeModifier} static S F() + {{ + S s1 = default; + scoped ref S r1 = ref s1; + scoped S s2 = default; + ref S r2 = ref s2; + r1 = ref r2; // 1 + r2 = s2; + return s1; + }} +}}"; + var comp = CreateCompilation(source, + options: (useUnsafe ? TestOptions.UnsafeReleaseDll : null)); + if (useUnsafe) + { + comp.VerifyEmitDiagnostics( + // (13,18): warning CS9080: Use of variable 'r2' in this context may expose referenced variables outside of their declaration scope + // r1 = ref r2; // 1 + Diagnostic(ErrorCode.WRN_EscapeVariable, "r2").WithArguments("r2").WithLocation(13, 18)); + } + else + { + comp.VerifyEmitDiagnostics( + // (13,18): error CS8352: Cannot use variable 'r2' in this context because it may expose referenced variables outside of their declaration scope + // r1 = ref r2; + Diagnostic(ErrorCode.ERR_EscapeVariable, "r2").WithArguments("r2").WithLocation(13, 18)); + } + } + // Breaking change in C#11: Cannot return an 'out' parameter by reference. [Fact] public void BreakingChange_ReturnOutByRef() @@ -9445,9 +9905,9 @@ static void F(ref R x) }"; comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( - // (7,9): error CS8350: This combination of arguments to 'A.F2(ref R, ref R)' is disallowed because it may expose variables referenced by parameter 'y2' outside of their declaration scope + // (7,9): error CS8350: This combination of arguments to 'A.F2(ref R, scoped ref R)' is disallowed because it may expose variables referenced by parameter 'y2' outside of their declaration scope // A.F2(ref x, ref y); - Diagnostic(ErrorCode.ERR_CallArgMixing, "A.F2(ref x, ref y)").WithArguments("A.F2(ref R, ref R)", "y2").WithLocation(7, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "A.F2(ref x, ref y)").WithArguments("A.F2(ref R, scoped ref R)", "y2").WithLocation(7, 9), // (7,25): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // A.F2(ref x, ref y); Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(7, 25)); @@ -9470,7 +9930,7 @@ static void verify(CSharpCompilation comp, bool useUpdatedEscapeRules) parameters = comp.GetMember("A.F4").Parameters; VerifyParameterSymbol(parameters[0], "out R x4", RefKind.Out, useUpdatedEscapeRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); - VerifyParameterSymbol(parameters[1], useUpdatedEscapeRules ? "out R y4" : "scoped out R y4", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(parameters[1], "out R y4", RefKind.Out, DeclarationScope.RefScoped); } } @@ -9573,13 +10033,13 @@ static void verify(CSharpCompilation comp, bool useUpdatedEscapeRules) VerifyParameterSymbol(localFunctions[2].Parameters[0], "in System.Int32 x3", RefKind.In, DeclarationScope.Unscoped); VerifyParameterSymbol(localFunctions[2].Parameters[1], "scoped in System.Int32 y3", RefKind.In, DeclarationScope.RefScoped); VerifyParameterSymbol(localFunctions[3].Parameters[0], "out System.Int32 x4", RefKind.Out, useUpdatedEscapeRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); - VerifyParameterSymbol(localFunctions[3].Parameters[1], useUpdatedEscapeRules ? "out System.Int32 y4" : "scoped out System.Int32 y4", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(localFunctions[3].Parameters[1], "out System.Int32 y4", RefKind.Out, DeclarationScope.RefScoped); VerifyParameterSymbol(localFunctions[4].Parameters[0], "ref R x5", RefKind.Ref, DeclarationScope.Unscoped); VerifyParameterSymbol(localFunctions[4].Parameters[1], "scoped ref R y5", RefKind.Ref, DeclarationScope.RefScoped); VerifyParameterSymbol(localFunctions[5].Parameters[0], "in R x6", RefKind.In, DeclarationScope.Unscoped); VerifyParameterSymbol(localFunctions[5].Parameters[1], "scoped in R y6", RefKind.In, DeclarationScope.RefScoped); VerifyParameterSymbol(localFunctions[6].Parameters[0], "out R x7", RefKind.Out, useUpdatedEscapeRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); - VerifyParameterSymbol(localFunctions[6].Parameters[1], useUpdatedEscapeRules ? "out R y7" : "scoped out R y7", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(localFunctions[6].Parameters[1], "out R y7", RefKind.Out, DeclarationScope.RefScoped); } } @@ -9643,19 +10103,19 @@ static void verify(CSharpCompilation comp, bool useUpdatedEscapeRules) verifyParameter(delegateTypesAndLambdas[2], 0, "in System.Int32", "x3", RefKind.In, DeclarationScope.Unscoped); verifyParameter(delegateTypesAndLambdas[2], 1, "scoped in System.Int32", "y3", RefKind.In, DeclarationScope.RefScoped); verifyParameter(delegateTypesAndLambdas[3], 0, "out System.Int32", "x4", RefKind.Out, useUpdatedEscapeRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); - verifyParameter(delegateTypesAndLambdas[3], 1, useUpdatedEscapeRules ? "out System.Int32" : "scoped out System.Int32", "y4", RefKind.Out, DeclarationScope.RefScoped); + verifyParameter(delegateTypesAndLambdas[3], 1, "out System.Int32", "y4", RefKind.Out, DeclarationScope.RefScoped); verifyParameter(delegateTypesAndLambdas[4], 0, "ref R", "x5", RefKind.Ref, DeclarationScope.Unscoped); verifyParameter(delegateTypesAndLambdas[4], 1, "scoped ref R", "y5", RefKind.Ref, DeclarationScope.RefScoped); verifyParameter(delegateTypesAndLambdas[5], 0, "in R", "x6", RefKind.In, DeclarationScope.Unscoped); verifyParameter(delegateTypesAndLambdas[5], 1, "scoped in R", "y6", RefKind.In, DeclarationScope.RefScoped); verifyParameter(delegateTypesAndLambdas[6], 0, "out R", "x7", RefKind.Out, useUpdatedEscapeRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); - verifyParameter(delegateTypesAndLambdas[6], 1, useUpdatedEscapeRules ? "out R" : "scoped out R", "y7", RefKind.Out, DeclarationScope.RefScoped); + verifyParameter(delegateTypesAndLambdas[6], 1, "out R", "y7", RefKind.Out, DeclarationScope.RefScoped); } static void verifyParameter((NamedTypeSymbol, LambdaSymbol) delegateTypeAndLambda, int parameterIndex, string expectedDisplayType, string expectedDisplayName, RefKind expectedRefKind, DeclarationScope expectedScope) { var (delegateType, lambda) = delegateTypeAndLambda; - VerifyParameterSymbol(delegateType.DelegateInvokeMethod.Parameters[parameterIndex], expectedDisplayType, expectedRefKind, expectedScope); + VerifyParameterSymbol(delegateType.DelegateInvokeMethod.Parameters[parameterIndex], $"{expectedDisplayType} arg{parameterIndex + 1}", expectedRefKind, expectedScope); VerifyParameterSymbol(lambda.Parameters[parameterIndex], $"{expectedDisplayType} {expectedDisplayName}", expectedRefKind, expectedScope); } @@ -9917,18 +10377,17 @@ static void Main() } }"; var comp = CreateCompilation(source); - // https://github.com/dotnet/roslyn/issues/62080: Lambda parameter r2 should be inferred as 'scoped R' rather than 'R'. comp.VerifyEmitDiagnostics( - // (9,17): error CS8986: The 'scoped' modifier of parameter 'r2' doesn't match target 'D2'. + // (9,23): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // D2 d2 = r2 => r2; - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "r2 => r2").WithArguments("r2", "D2").WithLocation(9, 17)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r2").WithArguments("scoped R").WithLocation(9, 23)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var lambdas = tree.GetRoot().DescendantNodes().OfType().Select(e => model.GetSymbolInfo(e).Symbol.GetSymbol()).ToArray(); VerifyParameterSymbol(lambdas[0].Parameters[0], "R r1", RefKind.None, DeclarationScope.Unscoped); - VerifyParameterSymbol(lambdas[1].Parameters[0], "R r2", RefKind.None, DeclarationScope.Unscoped); + VerifyParameterSymbol(lambdas[1].Parameters[0], "scoped R r2", RefKind.None, DeclarationScope.ValueScoped); } [Fact] @@ -10038,11 +10497,11 @@ static ref R F2(ref int i) } static ref R ReturnRef(scoped ref R r) => throw null; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (12,20): error CS8347: Cannot use a result of 'Program.ReturnRef(ref R)' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope + // (12,20): error CS8347: Cannot use a result of 'Program.ReturnRef(scoped ref R)' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope // return ref ReturnRef(ref r1); // 1 - Diagnostic(ErrorCode.ERR_EscapeCall, "ReturnRef(ref r1)").WithArguments("Program.ReturnRef(ref R)", "r").WithLocation(12, 20), + Diagnostic(ErrorCode.ERR_EscapeCall, "ReturnRef(ref r1)").WithArguments("Program.ReturnRef(scoped ref R)", "r").WithLocation(12, 20), // (12,34): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // return ref ReturnRef(ref r1); // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(12, 34)); @@ -10330,7 +10789,7 @@ ref struct R2 scoped ref int F3; }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,15): error CS0106: The modifier 'scoped' is not valid for this item // scoped R1 F1; @@ -10342,7 +10801,7 @@ ref struct R2 // scoped ref int F3; Diagnostic(ErrorCode.ERR_BadMemberFlag, "F3").WithArguments("scoped").WithLocation(6, 20)); - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,15): error CS0106: The modifier 'scoped' is not valid for this item // scoped R1 F1; @@ -10364,7 +10823,7 @@ ref struct R2 scoped private ref int F3; }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,12): error CS1585: Member modifier 'private' must precede the member type and name // scoped private R1 F1; @@ -10376,7 +10835,7 @@ ref struct R2 // scoped private ref int F3; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(6, 20)); - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (5,12): error CS1585: Member modifier 'private' must precede the member type and name // scoped private R1 F1; @@ -10484,8 +10943,8 @@ static void Main() } private static readonly SymbolDisplayFormat displayFormatWithScoped = SymbolDisplayFormat.TestFormat. - WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped). - AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef); + AddParameterOptions(SymbolDisplayParameterOptions.IncludeModifiers). + AddLocalOptions(SymbolDisplayLocalOptions.IncludeModifiers); private static void VerifyParameterSymbol(ParameterSymbol parameter, string expectedDisplayString, RefKind expectedRefKind, DeclarationScope expectedScope) { @@ -10519,6 +10978,9 @@ static void F(ref R r) scoped R r1 = default; scoped ref R r2 = ref r; scoped ref readonly R r5 = ref r; + scoped var r11 = (R)default; + scoped ref var r21 = ref r; + scoped ref readonly var r51 = ref r; } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); @@ -10531,7 +10993,17 @@ static void F(ref R r) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(8, 9), // (9,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // scoped ref readonly R r5 = ref r; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(9, 9)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(9, 9), + // (10,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // scoped var r11 = (R)default; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(10, 9), + // (11,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // scoped ref var r21 = ref r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(11, 9), + // (12,9): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // scoped ref readonly var r51 = ref r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(12, 9) + ); verify(comp); comp = CreateCompilation(source); @@ -10548,23 +11020,105 @@ static void verify(CSharpCompilation comp) VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); VerifyLocalSymbol(locals[1], "scoped ref R r2", RefKind.Ref, DeclarationScope.RefScoped); VerifyLocalSymbol(locals[2], "scoped ref readonly R r5", RefKind.RefReadOnly, DeclarationScope.RefScoped); + VerifyLocalSymbol(locals[3], "scoped R r11", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[4], "scoped ref R r21", RefKind.Ref, DeclarationScope.RefScoped); + VerifyLocalSymbol(locals[5], "scoped ref readonly R r51", RefKind.RefReadOnly, DeclarationScope.RefScoped); foreach (var decl in decls) { var type = ((VariableDeclarationSyntax)decl.Parent).Type; - Assert.Null(model.GetTypeInfo(type).Type); - Assert.Equal("R", model.GetSymbolInfo(type.SkipScoped(out _).SkipRef(out _)).Symbol.ToTestDisplayString()); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); - } - } - } - [Fact] - public void LocalScope_01_For() - { - var source = + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_Script() + { + var source = +@"#pragma warning disable 219 +scoped R r1 = default; +scoped var r11 = (R)default; + +ref struct R { } +"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script); + comp.VerifyEmitDiagnostics( + // (2,1): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // scoped R r1 = default; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped R").WithArguments("R").WithLocation(2, 1), + // (2,10): error CS0106: The modifier 'scoped' is not valid for this item + // scoped R r1 = default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "r1").WithArguments("scoped").WithLocation(2, 10), + // (3,1): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // scoped var r11 = (R)default; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped var").WithArguments("R").WithLocation(3, 1), + // (3,12): error CS0106: The modifier 'scoped' is not valid for this item + // scoped var r11 = (R)default; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "r11").WithArguments("scoped").WithLocation(3, 12) + ); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal(2, decls.Length); + + foreach (var decl in decls) + { + var f = model.GetDeclaredSymbol(decl).GetSymbol(); + + Assert.Equal(RefKind.None, f.RefKind); + Assert.Equal("Script.R", f.Type.ToTestDisplayString()); + + var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("Script.R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("Script.R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_For() + { + var source = @"#pragma warning disable 219 ref struct R { } class Program @@ -10607,11 +11161,831 @@ static void verify(CSharpCompilation comp) foreach (var decl in decls) { var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); Assert.Null(model.GetTypeInfo(type).Type); - Assert.Equal("R", model.GetSymbolInfo(type.SkipScoped(out _).SkipRef(out _)).Symbol.ToTestDisplayString()); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_Deconstruction() + { + var source = +@"#pragma warning disable 219 +ref struct R { } +class Program +{ + static void F(RR r) + { + (scoped R r1, var a) = r; + (scoped ref R r2, var b) = r; + (scoped ref readonly R r5, var c) = r; + (scoped R _, var d) = r; + (scoped var _, var e) = r; + (scoped ref R _, var f) = r; + (scoped ref var _, var g) = r; + (scoped ref readonly R _, var h) = r; + (scoped ref readonly var _, var i) = r; + (scoped var r11, var j) = r; + (scoped ref var r21, var k) = r; + (scoped ref readonly var r51, var l) = r; + } +} + +class RR +{ + public void Deconstruct(out R x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (7,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped R r1, var a) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(7, 10), + // (8,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(8, 10), + // (8,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 17), + // (9,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(9, 10), + // (9,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 17), + // (10,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped R _, var d) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 10), + // (11,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped var _, var e) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 10), + // (12,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 10), + // (12,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 17), + // (13,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 10), + // (13,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 17), + // (14,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(14, 10), + // (14,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(14, 17), + // (15,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(15, 10), + // (15,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(15, 17), + // (16,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped var r11, var j) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(16, 10), + // (17,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(17, 10), + // (17,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(17, 17), + // (18,10): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(18, 10), + // (18,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(18, 17) + ); + + verify(comp); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 17), + // (9,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 17), + // (10,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped R _, var d) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 10), + // (11,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped var _, var e) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 10), + // (12,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 10), + // (12,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 17), + // (13,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 10), + // (13,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 17), + // (14,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(14, 10), + // (14,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(14, 17), + // (15,10): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(15, 10), + // (15,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(15, 17), + // (17,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(17, 17), + // (18,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(18, 17) + ); + + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType(). + Where(d => d.Type is ScopedTypeSyntax && d.Designation is SingleVariableDesignationSyntax). + Select(d => d.Designation).ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + Assert.Equal(6, locals.Length); + + VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[1], "scoped R r2", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[2], "scoped R r5", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[3], "scoped R r11", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[4], "scoped R r21", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[5], "scoped R r51", RefKind.None, DeclarationScope.ValueScoped); + + foreach (var decl in decls) + { + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + + var discard = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(6, discard.Length); + + foreach (var decl in discard) + { + Assert.Null(model.GetDeclaredSymbol(decl)); + Assert.Null(model.GetSymbolInfo(decl).Symbol); + Assert.Null(model.GetTypeInfo(decl).Type); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_Deconstruction_Script() + { + var source = +@"#pragma warning disable 219 +RR r = new RR(); +(scoped R r1, var a) = r; +(scoped ref R r2, var b) = r; +(scoped ref readonly R r5, var c) = r; +(scoped R _, var d) = r; +(scoped var _, var e) = r; +(scoped ref R _, var f) = r; +(scoped ref var _, var g) = r; +(scoped ref readonly R _, var h) = r; +(scoped ref readonly var _, var i) = r; +(scoped var r11, var j) = r; +(scoped ref var r21, var k) = r; +(scoped ref readonly var r51, var l) = r; + +ref struct R { } + +class RR +{ + public void Deconstruct(out R x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script); + comp.VerifyEmitDiagnostics( + // (3,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped R r1, var a) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped R").WithArguments("R").WithLocation(3, 2), + // (3,2): error CS1073: Unexpected token 'scoped' + // (scoped R r1, var a) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(3, 2), + // (4,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref R").WithArguments("R").WithLocation(4, 2), + // (4,2): error CS1073: Unexpected token 'scoped' + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(4, 2), + // (4,9): error CS1073: Unexpected token 'ref' + // (scoped ref R r2, var b) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 9), + // (5,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref readonly R").WithArguments("R").WithLocation(5, 2), + // (5,2): error CS1073: Unexpected token 'scoped' + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(5, 2), + // (5,9): error CS1073: Unexpected token 'ref' + // (scoped ref readonly R r5, var c) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(5, 9), + // (6,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped R _, var d) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(6, 2), + // (7,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped var _, var e) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(7, 2), + // (8,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(8, 2), + // (8,9): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref R _, var f) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 9), + // (9,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 2), + // (9,9): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var _, var g) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 9), + // (10,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 2), + // (10,9): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly R _, var h) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(10, 9), + // (11,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 2), + // (11,9): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref readonly var _, var i) = r; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(11, 9), + // (12,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped var r11, var j) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped var").WithArguments("R").WithLocation(12, 2), + // (12,2): error CS1073: Unexpected token 'scoped' + // (scoped var r11, var j) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(12, 2), + // (13,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref var").WithArguments("R").WithLocation(13, 2), + // (13,2): error CS1073: Unexpected token 'scoped' + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(13, 2), + // (13,9): error CS1073: Unexpected token 'ref' + // (scoped ref var r21, var k) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(13, 9), + // (14,2): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref readonly var").WithArguments("R").WithLocation(14, 2), + // (14,2): error CS1073: Unexpected token 'scoped' + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(14, 2), + // (14,9): error CS1073: Unexpected token 'ref' + // (scoped ref readonly var r51, var l) = r; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(14, 9) + ); + + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType(). + Where(d => d.Type is ScopedTypeSyntax && d.Designation is SingleVariableDesignationSyntax). + Select(d => d.Designation).ToArray(); + + Assert.Equal(6, decls.Length); + + foreach (var decl in decls) + { + var f = model.GetDeclaredSymbol(decl).GetSymbol(); + + Assert.Equal(RefKind.None, f.RefKind); + Assert.Equal("Script.R", f.Type.ToTestDisplayString()); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("Script.R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("Script.R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + + var discard = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(6, discard.Length); + + foreach (var decl in discard) + { + Assert.Null(model.GetDeclaredSymbol(decl)); + Assert.Null(model.GetSymbolInfo(decl).Symbol); + Assert.Null(model.GetTypeInfo(decl).Type); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("Script.R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("Script.R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_OutVar() + { + var source = +@"#pragma warning disable 219 +ref struct R { } +class Program +{ + static void F(ref R r) + { + M1(out scoped R r1); + M1(out scoped ref R r2); + M1(out scoped ref readonly R r5); + M1(out scoped R _); + M1(out scoped var _); + M1(out scoped ref R _); + M1(out scoped ref var _); + M1(out scoped ref readonly R _); + M1(out scoped ref readonly var _); + M1(out scoped var r11); + M1(out scoped ref var r21); + M1(out scoped ref readonly var r51); + + M1(out scoped _); + M1(out scoped scoped _); + } + + static void M1(out R r) => throw null; +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (7,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped R r1); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(7, 16), + // (8,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(8, 16), + // (8,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref R").WithLocation(8, 23), + // (9,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(9, 16), + // (9,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly R").WithLocation(9, 23), + // (10,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 16), + // (11,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 16), + // (12,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 16), + // (12,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref R").WithLocation(12, 23), + // (13,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 16), + // (13,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(13, 23), + // (14,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(14, 16), + // (14,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly R").WithLocation(14, 23), + // (15,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(15, 16), + // (15,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly var").WithLocation(15, 23), + // (16,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped var r11); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(16, 16), + // (17,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(17, 16), + // (17,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(17, 23), + // (18,16): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(18, 16), + // (18,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly var").WithLocation(18, 23), + // (20,16): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // M1(out scoped _); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(20, 16), + // (21,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped scoped _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(21, 16), + // (21,23): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // M1(out scoped scoped _); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(21, 23) + ); + + verify(comp); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref R").WithLocation(8, 23), + // (9,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly R").WithLocation(9, 23), + // (10,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 16), + // (11,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 16), + // (12,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 16), + // (12,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref R").WithLocation(12, 23), + // (13,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 16), + // (13,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(13, 23), + // (14,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(14, 16), + // (14,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly R").WithLocation(14, 23), + // (15,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(15, 16), + // (15,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly var").WithLocation(15, 23), + // (17,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(17, 23), + // (18,23): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly var").WithLocation(18, 23), + // (20,16): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // M1(out scoped _); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(20, 16), + // (21,16): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped scoped _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(21, 16), + // (21,23): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // M1(out scoped scoped _); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(21, 23) + ); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + Assert.Equal(6, locals.Length); + + VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[1], "scoped R r2", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[2], "scoped R r5", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[3], "scoped R r11", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[4], "scoped R r21", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[5], "scoped R r51", RefKind.None, DeclarationScope.ValueScoped); + + foreach (var decl in decls) + { + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + + var discard = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(8, discard.Length); + + for (int i = 0; i < 6; i++) + { + var decl = discard[i]; + + Assert.Null(model.GetDeclaredSymbol(decl)); + Assert.Null(model.GetSymbolInfo(decl).Symbol); + Assert.Null(model.GetTypeInfo(decl).Type); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_OutVar_Script() + { + var source = +@"#pragma warning disable 219 + +M1(out scoped R r1); +M1(out scoped ref R r2); +M1(out scoped ref readonly R r5); +M1(out scoped R _); +M1(out scoped var _); +M1(out scoped ref R _); +M1(out scoped ref var _); +M1(out scoped ref readonly R _); +M1(out scoped ref readonly var _); +M1(out scoped var r11); +M1(out scoped ref var r21); +M1(out scoped ref readonly var r51); + +static void M1(out R r) => throw null; + +ref struct R { } +"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script); + comp.VerifyEmitDiagnostics( + // (3,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped R r1); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped R").WithArguments("R").WithLocation(3, 8), + // (3,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped R r1); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(3, 8), + // (4,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref R").WithArguments("R").WithLocation(4, 8), + // (4,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(4, 8), + // (4,15): error CS1073: Unexpected token 'ref' + // M1(out scoped ref R r2); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 15), + // (5,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref readonly R").WithArguments("R").WithLocation(5, 8), + // (5,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(5, 8), + // (5,15): error CS1073: Unexpected token 'ref' + // M1(out scoped ref readonly R r5); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(5, 15), + // (6,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(6, 8), + // (7,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(7, 8), + // (8,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(8, 8), + // (8,15): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref R").WithLocation(8, 15), + // (9,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 8), + // (9,15): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(9, 15), + // (10,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(10, 8), + // (10,15): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly R _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly R").WithLocation(10, 15), + // (11,8): error CS9061: The 'scoped' modifier cannot be used with discard. + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 8), + // (11,15): error CS8388: An out variable cannot be declared as a ref local + // M1(out scoped ref readonly var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref readonly var").WithLocation(11, 15), + // (12,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped var r11); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped var").WithArguments("R").WithLocation(12, 8), + // (12,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped var r11); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(12, 8), + // (13,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref var").WithArguments("R").WithLocation(13, 8), + // (13,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(13, 8), + // (13,15): error CS1073: Unexpected token 'ref' + // M1(out scoped ref var r21); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(13, 15), + // (14,8): error CS8345: Field or auto-implemented property cannot be of type 'R' unless it is an instance member of a ref struct. + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "scoped ref readonly var").WithArguments("R").WithLocation(14, 8), + // (14,8): error CS1073: Unexpected token 'scoped' + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "scoped").WithArguments("scoped").WithLocation(14, 8), + // (14,15): error CS1073: Unexpected token 'ref' + // M1(out scoped ref readonly var r51); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(14, 15) + ); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal(6, decls.Length); + + + foreach (var decl in decls) + { + var f = model.GetDeclaredSymbol(decl).GetSymbol(); + + Assert.Equal(RefKind.None, f.RefKind); + Assert.Equal("Script.R", f.Type.ToTestDisplayString()); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("Script.R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("Script.R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + + var discard = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(6, discard.Length); + + foreach (var decl in discard) + { + Assert.Null(model.GetDeclaredSymbol(decl)); + Assert.Null(model.GetSymbolInfo(decl).Symbol); + Assert.Null(model.GetTypeInfo(decl).Type); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("Script.R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("Script.R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } } } @@ -10798,6 +12172,108 @@ static void verify(CSharpCompilation comp) } } + [Fact] + public void LocalScope_04_Deconstruction() + { + var source = +@" +var r = new RR(); +(scoped s1, var a) = r; +(@scoped s3, var b) = r; +(scoped scoped s4, var c) = r; +(scoped @scoped s6, var d) = r; + +(scoped _, var e) = r; +(scoped scoped _, var f) = r; + +ref struct @scoped { } + +class RR +{ + public void Deconstruct(out @scoped x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (5,2): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped scoped s4, var c) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(5, 2), + // (6,2): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // (scoped @scoped s6, var d) = r; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(6, 2), + // (9,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped scoped _, var f) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 2) + ); + verify(comp); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,2): error CS9061: The 'scoped' modifier cannot be used with discard. + // (scoped scoped _, var f) = r; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 2) + ); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + VerifyLocalSymbol(locals[0], "scoped s1", RefKind.None, DeclarationScope.Unscoped); + VerifyLocalSymbol(locals[2], "scoped s3", RefKind.None, DeclarationScope.Unscoped); + VerifyLocalSymbol(locals[4], "scoped scoped s4", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[6], "scoped scoped s6", RefKind.None, DeclarationScope.ValueScoped); + } + } + + [Fact] + public void LocalScope_04_OutVar() + { + var source = +@" +M(out scoped s1); +M(out @scoped s3); +M(out scoped scoped s4); +M(out scoped @scoped s6); + +void M(out scoped x) => throw null; + +ref struct @scoped { } +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (4,7): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M(out scoped scoped s4); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(4, 7), + // (5,7): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // M(out scoped @scoped s6); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(5, 7) + ); + verify(comp); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + Assert.Equal(4, locals.Length); + + VerifyLocalSymbol(locals[0], "scoped s1", RefKind.None, DeclarationScope.Unscoped); + VerifyLocalSymbol(locals[1], "scoped s3", RefKind.None, DeclarationScope.Unscoped); + VerifyLocalSymbol(locals[2], "scoped scoped s4", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[3], "scoped scoped s6", RefKind.None, DeclarationScope.ValueScoped); + } + } + [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] @@ -10832,15 +12308,57 @@ public void LocalScope_05_For(LanguageVersion langVersion) } "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); - comp.VerifyDiagnostics( - // (1,11): warning CS0219: The variable 'scoped' is assigned but its value is never used - // for (bool scoped;;) { - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "scoped").WithArguments("scoped").WithLocation(1, 11) - ); + comp.VerifyDiagnostics( + // (1,11): warning CS0219: The variable 'scoped' is assigned but its value is never used + // for (bool scoped;;) { + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "scoped").WithArguments("scoped").WithLocation(1, 11) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + VerifyLocalSymbol(locals[0], "System.Boolean scoped", RefKind.None, DeclarationScope.Unscoped); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void LocalScope_05_Deconstruction(LanguageVersion langVersion) + { + var source = +@" +(bool scoped, var x) = (true, 0); +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + VerifyLocalSymbol(locals[0], "System.Boolean scoped", RefKind.None, DeclarationScope.Unscoped); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void LocalScope_05_OutVar(LanguageVersion langVersion) + { + var source = +@" +M(out bool scoped); + +void M(out bool x) => throw null; +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); VerifyLocalSymbol(locals[0], "System.Boolean scoped", RefKind.None, DeclarationScope.Unscoped); @@ -10889,7 +12407,21 @@ static void verifyModel(CSharpCompilation comp) foreach (var decl in decls) { - var type = ((VariableDeclarationSyntax)decl.Parent).Type.SkipScoped(out _).SkipRef(out _); + var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } @@ -10939,7 +12471,168 @@ static void verifyModel(CSharpCompilation comp) foreach (var decl in decls) { - var type = ((VariableDeclarationSyntax)decl.Parent).Type.SkipScoped(out _).SkipRef(out _); + var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_06_Deconstruction() + { + var source = +@"ref struct R { } +class Program +{ + static void M(R r0) + { + (scoped var r1, var a) = new RR(new R()); + (scoped ref var r3, var b) = new RR(ref r0); + + scoped R r4; + int c; + (r4, c) = new RR(ref r0); + } +} + +ref struct RR +{ + public RR(R x){} + public RR(ref R x){} + public void Deconstruct(out R x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,17): error CS9072: A deconstruction variable cannot be declared as a ref local + // (scoped ref var r3, var b) = new RR(ref r0); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(7, 17) + ); + + verifyModel(comp); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularDefault.WithFeature("run-nullable-analysis", "never")); + verifyModel(comp); + + static void verifyModel(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + for (int i = 0; i < 3; i += 2) + { + Assert.True(locals[i].IsVar); + Assert.Equal("R", locals[i].Type.ToTestDisplayString()); + } + + VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[2], "scoped R r3", RefKind.None, DeclarationScope.ValueScoped); + + for (int i = 0; i < 3; i += 2) + { + var decl = decls[i]; + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_06_OutVar() + { + var source = +@"ref struct R { } +class Program +{ + static void M(R r0) + { + M1(out scoped var r1, new R()); + M2(out scoped ref var r3, ref r0); + + scoped R r4; + M2(out r4, ref r0); + } + + static void M1(out R y, R x) => throw null; + static void M2(out R y, ref R x) => throw null; +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,23): error CS8388: An out variable cannot be declared as a ref local + // M2(out scoped ref var r3, ref r0); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(7, 23) + ); + + verifyModel(comp); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularDefault.WithFeature("run-nullable-analysis", "never")); + verifyModel(comp); + + static void verifyModel(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + foreach (SourceLocalSymbol local in locals) + { + Assert.True(local.IsVar); + Assert.Equal("R", local.Type.ToTestDisplayString()); + } + + VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[1], "scoped R r3", RefKind.None, DeclarationScope.ValueScoped); + + foreach (var decl in decls) + { + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } @@ -11146,6 +12839,51 @@ public S(ref int i) { } comp.VerifyDiagnostics(); } + [Fact] + public void LocalScope_10_Deconstruction() + { + var source = +@"{ + int i = 0; + S s1 = new S(ref i); + (scoped S s2, var a) = s1; + scoped S s3; + int b; + (s3, b) = s1; +} +ref struct S +{ + public S(ref int i) { } + + public void Deconstruct(out S x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void LocalScope_10_OutVar() + { + var source = +@"{ + int i = 0; + S s1 = new S(ref i); + s1.M(out scoped S s2); + scoped S s3; + s1.M(out s3); +} +ref struct S +{ + public S(ref int i) { } + + public void M(out S x) => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + [Fact] public void LocalScope_11() { @@ -11369,7 +13107,7 @@ static void Main() S s3 = s0; s0 = s3; - + break; } } @@ -11384,9 +13122,160 @@ public S(ref int i) { } // (10,18): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope // s0 = s1; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(10, 18), - // (13,18): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // s0 = s2; // 2 - Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(13, 18) + // (13,18): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s2; // 2 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(13, 18) + ); + } + + [Fact] + public void LocalScope_12_Deconstruct() + { + var source = +@"class Program +{ + static void Main() + { + int i0 = 0; + S s0 = new S(ref i0); + { + int i1 = 1; + (S s1, var a) = new S(ref i1); + s0 = s1; + } + { + (scoped S s2, var b) = s0; + s0 = s2; + } + { + (S s3, var c) = s0; + s0 = s3; + } + { + int i1 = 1; + S s11; + int d; + (s11, d) = new S(ref i1); + s0 = s11; + } + { + scoped S s21; + int e; + (s21, e) = s0; + s0 = s21; + } + { + S s31; + int f; + (s31, f) = s0; + s0 = s31; + } + } +} +ref struct S +{ + public S(ref int i) { } + public void Deconstruct(out S x, out int y) => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,18): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(10, 18), + // (14,18): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s2; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(14, 18), + // (24,13): error CS8352: Cannot use variable '(s11, d) = new S(ref i1)' in this context because it may expose referenced variables outside of their declaration scope + // (s11, d) = new S(ref i1); + Diagnostic(ErrorCode.ERR_EscapeVariable, "(s11, d) = new S(ref i1)").WithArguments("(s11, d) = new S(ref i1)").WithLocation(24, 13), + // (24,24): error CS8350: This combination of arguments to 'S.Deconstruct(out S, out int)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // (s11, d) = new S(ref i1); + Diagnostic(ErrorCode.ERR_CallArgMixing, "new S(ref i1)").WithArguments("S.Deconstruct(out S, out int)", "this").WithLocation(24, 24), + // (31,18): error CS8352: Cannot use variable 's21' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s21; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s21").WithArguments("s21").WithLocation(31, 18), + // (36,13): error CS8352: Cannot use variable '(s31, f) = s0' in this context because it may expose referenced variables outside of their declaration scope + // (s31, f) = s0; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(s31, f) = s0").WithArguments("(s31, f) = s0").WithLocation(36, 13), + // (36,24): error CS8350: This combination of arguments to 'S.Deconstruct(out S, out int)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope + // (s31, f) = s0; + Diagnostic(ErrorCode.ERR_CallArgMixing, "s0").WithArguments("S.Deconstruct(out S, out int)", "this").WithLocation(36, 24) + ); + } + + [Fact] + public void LocalScope_12_OutVar() + { + var source = +@"class Program +{ + static void Main() + { + int i0 = 0; + S s0 = new S(ref i0); + { + int i1 = 1; + (new S(ref i1)).M(out S s1); + s0 = s1; + } + { + s0.M(out scoped S s2); + s0 = s2; + } + { + s0.M(out S s3); + s0 = s3; + } + { + scoped S s21; + s0.M(out s21); + s0 = s21; + } + + { + int i1 = 1; + (new S(ref i1)).M(out scoped S s4); + s0 = s4; + } + { + int i1 = 1; + (new S(ref i1)).M(out var s5); + s0 = s5; + } + { + int i1 = 1; + (new S(ref i1)).M(out scoped var s7); + s0 = s7; + } + } +} +ref struct S +{ + public S(ref int i) { } + public void M(out S x) => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,18): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(10, 18), + // (14,18): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s2; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(14, 18), + // (23,18): error CS8352: Cannot use variable 's21' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s21; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s21").WithArguments("s21").WithLocation(23, 18), + // (29,18): error CS8352: Cannot use variable 's4' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s4; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s4").WithArguments("s4").WithLocation(29, 18), + // (34,18): error CS8352: Cannot use variable 's5' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s5; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s5").WithArguments("s5").WithLocation(34, 18), + // (39,18): error CS8352: Cannot use variable 's7' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s7; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s7").WithArguments("s7").WithLocation(39, 18) ); } @@ -11504,6 +13393,62 @@ static void Main() ); } + [Fact] + public void ScopedRefAndRefStructOnly_06_Deconstruct() + { + var source = +@"#pragma warning disable CS0219 // The variable is assigned but its value is never used +struct S { } +class Program +{ + static void Main() + { + (scoped var y1, var a) = (new S(), 0); + (var y2, var b) = (new S(), 1); + (scoped S y3, var c) = (new S(), 0); + (S y4, var d) = (new S(), 1); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,17): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // (scoped var y1, var a) = (new S(), 0); + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "var").WithLocation(7, 17), + // (9,17): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // (scoped S y3, var c) = (new S(), 0); + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "S").WithLocation(9, 17) + ); + } + + [Fact] + public void ScopedRefAndRefStructOnly_06_OutVar() + { + var source = +@"#pragma warning disable CS0219 // The variable is assigned but its value is never used +struct S { } +class Program +{ + static void Main() + { + M(out scoped var y1); + M(out var y2); + M(out scoped S y3); + M(out S y4); + } + + static void M(out S x) => throw null; +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,22): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // M(out scoped var y1); + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "var").WithLocation(7, 22), + // (9,22): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // M(out scoped S y3); + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "S").WithLocation(9, 22) + ); + } + [Fact] public void LocalScopeAndInitializer_01() { @@ -11617,11 +13562,288 @@ static void verify(CSharpCompilation comp) foreach (var decl in decls) { var type = decl.Type; - Assert.Null(model.GetTypeInfo(type).Type); - Assert.Equal("R", model.GetSymbolInfo(type.SkipScoped(out _).SkipRef(out _)).Symbol.ToTestDisplayString()); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + } + + [Fact] + public void LocalScope_01_Foreach_Deconstruction() + { + var source = +@"#pragma warning disable 219 + +class Program +{ + static void F(ref R r) + { + foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + + foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + } +} + +ref struct R +{ + public void Deconstruct(out R x, out R y) => throw null; +} + +class Enumerable1 +{ + public Enumerator1 GetEnumerator() => default; +} + +class Enumerator1 +{ + public R Current => default; + public bool MoveNext() => false; +} + +class Enumerable2 +{ + public Enumerable2(ref R x) {} + public Enumerator2 GetEnumerator() => default; +} + +class Enumerator2 +{ + public ref R Current => throw null; + public bool MoveNext() => false; +} +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (7,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(7, 19), + // (7,32): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(7, 32), + // (8,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(8, 19), + // (8,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 26), + // (8,36): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(8, 36), + // (8,43): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 43), + // (9,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(9, 19), + // (9,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 26), + // (9,45): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 45), + // (9,52): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 52), + // (11,18): error CS8352: Cannot use variable '(scoped var r11, scoped R _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped var r11, scoped R _)").WithArguments("(scoped var r11, scoped R _)").WithLocation(11, 18), + // (11,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(11, 19), + // (11,35): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 35), + // (11,50): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable1()").WithArguments("R.Deconstruct(out R, out R)", "y").WithLocation(11, 50), + // (12,18): error CS8352: Cannot use variable '(scoped ref var r21, scoped ref R _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped ref var r21, scoped ref R _)").WithArguments("(scoped ref var r21, scoped ref R _)").WithLocation(12, 18), + // (12,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(12, 19), + // (12,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 26), + // (12,39): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 39), + // (12,46): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 46), + // (12,58): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable2(ref r)").WithArguments("R.Deconstruct(out R, out R)", "y").WithLocation(12, 58), + // (13,18): error CS8352: Cannot use variable '(scoped ref readonly var r51, scoped ref readonly R _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped ref readonly var r51, scoped ref readonly R _)").WithArguments("(scoped ref readonly var r51, scoped ref readonly R _)").WithLocation(13, 18), + // (13,19): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "scoped").WithArguments("ref fields", "11.0").WithLocation(13, 19), + // (13,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 26), + // (13,48): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 48), + // (13,55): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 55), + // (13,76): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable2(ref r)").WithArguments("R.Deconstruct(out R, out R)", "y").WithLocation(13, 76) + ); + + verify(comp); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,18): error CS8352: Cannot use variable '(scoped R r1, scoped var _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped R r1, scoped var _)").WithArguments("(scoped R r1, scoped var _)").WithLocation(7, 18), + // (7,32): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(7, 32), + // (7,49): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // foreach ((scoped R r1, scoped var _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable1()").WithArguments("R.Deconstruct(out R, out R)", "x").WithLocation(7, 49), + // (8,18): error CS8352: Cannot use variable '(scoped ref R r2, scoped ref var _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped ref R r2, scoped ref var _)").WithArguments("(scoped ref R r2, scoped ref var _)").WithLocation(8, 18), + // (8,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 26), + // (8,36): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(8, 36), + // (8,43): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 43), + // (8,57): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // foreach ((scoped ref R r2, scoped ref var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable2(ref r)").WithArguments("R.Deconstruct(out R, out R)", "x").WithLocation(8, 57), + // (9,18): error CS8352: Cannot use variable '(scoped ref readonly R r5, scoped ref readonly var _)' in this context because it may expose referenced variables outside of their declaration scope + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_EscapeVariable, "(scoped ref readonly R r5, scoped ref readonly var _)").WithArguments("(scoped ref readonly R r5, scoped ref readonly var _)").WithLocation(9, 18), + // (9,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 26), + // (9,45): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(9, 45), + // (9,52): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(9, 52), + // (9,75): error CS8350: This combination of arguments to 'R.Deconstruct(out R, out R)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // foreach ((scoped ref readonly R r5, scoped ref readonly var _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_CallArgMixing, "new Enumerable2(ref r)").WithArguments("R.Deconstruct(out R, out R)", "x").WithLocation(9, 75), + // (11,35): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped var r11, scoped R _) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(11, 35), + // (12,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 26), + // (12,39): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(12, 39), + // (12,46): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref var r21, scoped ref R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(12, 46), + // (13,26): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 26), + // (13,48): error CS9061: The 'scoped' modifier cannot be used with discard. + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_ScopedDiscard, "scoped").WithLocation(13, 48), + // (13,55): error CS9072: A deconstruction variable cannot be declared as a ref local + // foreach ((scoped ref readonly var r51, scoped ref readonly R _) in new Enumerable2(ref r)) break; + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(13, 55) + ); + verify(comp); + + static void verify(CSharpCompilation comp) + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + var locals = decls.Select(d => model.GetDeclaredSymbol(d).GetSymbol()).ToArray(); + + VerifyLocalSymbol(locals[0], "scoped R r1", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[1], "scoped R r2", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[2], "scoped R r5", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[3], "scoped R r11", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[4], "scoped R r21", RefKind.None, DeclarationScope.ValueScoped); + VerifyLocalSymbol(locals[5], "scoped R r51", RefKind.None, DeclarationScope.ValueScoped); + + foreach (var decl in decls) + { + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + + var discard = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(6, discard.Length); + + foreach (var decl in discard) + { + Assert.Null(model.GetDeclaredSymbol(decl)); + Assert.Null(model.GetSymbolInfo(decl).Symbol); + Assert.Null(model.GetTypeInfo(decl).Type); + + var type = ((DeclarationExpressionSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } } } @@ -11779,7 +14001,21 @@ static void verifyModel(CSharpCompilation comp) foreach (var decl in decls) { - var type = decl.Type.SkipScoped(out _).SkipRef(out _); + var type = decl.Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } @@ -11868,8 +14104,10 @@ class Enumerator1 comp.VerifyDiagnostics(); } - [Fact] - public void LocalScope_10_Foreach_02() + [Theory] + [InlineData("class")] + [InlineData("ref struct")] + public void LocalScope_10_Foreach_02(string kind) { var source = @"int i = 0; @@ -11882,7 +14120,7 @@ ref struct S public S(ref int i) { } } -class Enumerable1 +" + kind + @" Enumerable1 { public static Enumerable1 Create(ref int p) => default; public Enumerator1 GetEnumerator() => default; @@ -11908,33 +14146,47 @@ static void Main() { S s0 = default; scoped ref S r0 = ref s0; - foreach (scoped ref S r1 in Enumerable1.Create(ref s0)) { + foreach (scoped ref S r1 in ClassEnumerable.Create(ref s0)) { r0 = ref r1; // 1 break; } - foreach (scoped ref S r2 in Enumerable1.Create(ref s0)) { + foreach (scoped ref S r2 in ClassEnumerable.Create(ref s0)) { r0 = ref r2; // 2 break; } - foreach (ref S r3 in Enumerable1.Create(ref s0)) { + foreach (ref S r3 in ClassEnumerable.Create(ref s0)) { r0 = ref r3; break; } - foreach (ref S r4 in Enumerable1.Create(ref s0)) { + foreach (ref S r4 in ClassEnumerable.Create(ref s0)) { r0 = ref r4; break; } + foreach (scoped ref S r5 in RefStructEnumerable.Create(ref s0)) { + r0 = ref r5; // 3 + break; + } + foreach (ref S r6 in RefStructEnumerable.Create(ref s0)) { + r0 = ref r6; // 4 + break; + } } } ref struct S { } -class Enumerable1 +class ClassEnumerable { - public static Enumerable1 Create(ref S p) => default; - public Enumerator1 GetEnumerator() => default; + public static ClassEnumerable Create(ref S p) => default; + public ClassEnumerator GetEnumerator() => default; } -class Enumerator1 +ref struct RefStructEnumerable +{ + public static RefStructEnumerable Create(ref S p) => default; + public ClassEnumerator GetEnumerator() => default; +} + +class ClassEnumerator { public ref S Current => throw null; public bool MoveNext() => false; @@ -11947,11 +14199,19 @@ class Enumerator1 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r0 = ref r1").WithArguments("r0", "r1").WithLocation(8, 13), // (12,13): error CS8374: Cannot ref-assign 'r2' to 'r0' because 'r2' has a narrower escape scope than 'r0'. // r0 = ref r2; // 2 - Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r0 = ref r2").WithArguments("r0", "r2").WithLocation(12, 13)); + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r0 = ref r2").WithArguments("r0", "r2").WithLocation(12, 13), + // (24,13): error CS8374: Cannot ref-assign 'r5' to 'r0' because 'r5' has a narrower escape scope than 'r0'. + // r0 = ref r5; // 3 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r0 = ref r5").WithArguments("r0", "r5").WithLocation(24, 13), + // (28,22): error CS8352: Cannot use variable 'r6' in this context because it may expose referenced variables outside of their declaration scope + // r0 = ref r6; // 4 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r6").WithArguments("r6").WithLocation(28, 22)); } - [Fact] - public void LocalScope_11_Foreach_02() + [Theory] + [InlineData("class")] + [InlineData("ref struct")] + public void LocalScope_11_Foreach_02(string kind) { var source = @"class Program @@ -11979,7 +14239,7 @@ static void Main() } ref struct S { } -class Enumerable1 +" + kind + @" Enumerable1 { public static Enumerable1 Create(ref S p) => default; public Enumerator1 GetEnumerator() => default; @@ -12022,12 +14282,12 @@ static void Main() { int i1 = 1; foreach (S s1 in Enumerable1.Create(ref i1)) { - s0 = s1; // <-- An error is expected here, see https://github.com/dotnet/roslyn/issues/64218 + s0 = s1; break; } } foreach (scoped S s2 in Enumerable1.Create(s0)) { - s0 = s2; // 2 + s0 = s2; // 1 break; } foreach (S s3 in Enumerable1.Create(s0)) { @@ -12057,13 +14317,15 @@ class Enumerator1 var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (15,18): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope - // s0 = s2; // 2 + // s0 = s2; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(15, 18) ); } - [Fact] - public void LocalScope_12_Foreach_02() + [Theory] + [InlineData("class")] + [InlineData("ref struct")] + public void LocalScope_12_Foreach_02(string kind) { var source = @"class Program @@ -12082,7 +14344,7 @@ static void Main() S s3 = s0; s0 = s3; - + break; } } @@ -12091,14 +14353,118 @@ ref struct S { public S(ref int i) { } } - -class Enumerable1 + +" + kind + @" Enumerable1 +{ + public static Enumerable1 Create(ref int p) => default; + public Enumerator1 GetEnumerator() => default; +} + +class Enumerator1 +{ + public S Current => throw null; + public bool MoveNext() => false; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' + // s0 = s1; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(10, 13), + // (13,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' + // s0 = s2; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(13, 13), + // (16,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' + // s0 = s3; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(16, 13) + ); + } + + [Fact] + [WorkItem(64218, "https://github.com/dotnet/roslyn/issues/64218")] + public void LocalScope_12_Foreach_03() + { + var source = +@"class Program +{ + static void Main() + { + int i0 = 0; + S s0 = new S(ref i0); + { + int i1 = 1; + foreach (S s1 in Enumerable1.Create(ref i1)) { + s0 = s1; + break; + } + } + } +} +ref struct S +{ + public S(ref int i) { } +} +ref struct Enumerable1 +{ + public static Enumerable1 Create(ref int p) => default; + public Enumerator1 GetEnumerator() => default; +} +ref struct Enumerator1 +{ + public S Current => throw null; + public bool MoveNext() => false; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,22): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(10, 22) + ); + } + + [Fact] + [WorkItem(64218, "https://github.com/dotnet/roslyn/issues/64218")] + public void LocalScope_12_Foreach_04() + { + var source = +@"class Program +{ + static void Main() + { + int i0 = 0; + S s0 = new S(ref i0); + { + int i1 = 1; + foreach (S s1 in RefStructEnumerable.Create(ref i1)) { + s0 = s1; + break; + } + } + { + int i2 = 1; + foreach (S s2 in ClassEnumerable.Create(ref i2)) { + s0 = s2; + break; + } + } + } +} +ref struct S +{ + public S(ref int i) { } +} +ref struct RefStructEnumerable { - public static Enumerable1 Create(ref int p) => default; - public Enumerator1 GetEnumerator() => default; + public static RefStructEnumerable Create(ref int p) => default; + public ClassEnumerator GetEnumerator() => default; } - -class Enumerator1 +class ClassEnumerable +{ + public static ClassEnumerable Create(ref int p) => default; + public ClassEnumerator GetEnumerator() => default; +} +class ClassEnumerator { public S Current => throw null; public bool MoveNext() => false; @@ -12106,16 +14472,9 @@ class Enumerator1 "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (10,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' - // s0 = s1; - Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(10, 13), - // (13,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' - // s0 = s2; - Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(13, 13), - // (16,13): error CS1656: Cannot assign to 's0' because it is a 'foreach iteration variable' - // s0 = s3; - Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "s0").WithArguments("s0", "foreach iteration variable").WithLocation(16, 13) - ); + // (10,22): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope + // s0 = s1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(10, 22)); } [Fact] @@ -12130,6 +14489,8 @@ static void Main() { foreach (scoped var y1 in new Enumerable1()) break; foreach (scoped ref var y3 in new Enumerable2()) break; + foreach (scoped S y1 in new Enumerable1()) break; + foreach (scoped ref S y3 in new Enumerable2()) break; } } @@ -12159,7 +14520,50 @@ class Enumerator2 comp.VerifyDiagnostics( // (7,25): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // foreach (scoped var y1 in new Enumerable1()) break; - Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "var").WithLocation(7, 25) + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "var").WithLocation(7, 25), + // (9,25): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // foreach (scoped S y1 in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "S").WithLocation(9, 25) + ); + } + + [Fact] + public void ScopedRefAndRefStructOnly_06_Foreach_Deconstruction() + { + var source = +@" +class Program +{ + static void Main() + { + foreach ((scoped var y1, scoped S y2) in new Enumerable1()) break; + } +} + +struct S +{ + public void Deconstruct(out S x, out S y) => throw null; +} + +class Enumerable1 +{ + public Enumerator1 GetEnumerator() => default; +} + +class Enumerator1 +{ + public S Current => default; + public bool MoveNext() => false; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,26): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // foreach ((scoped var y1, scoped S y2) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "var").WithLocation(6, 26), + // (6,41): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // foreach ((scoped var y1, scoped S y2) in new Enumerable1()) break; + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "S").WithLocation(6, 41) ); } @@ -12232,12 +14636,12 @@ class Program }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (4,39): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (4,39): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R Implicit1(scoped R r) => r; - Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("R").WithLocation(4, 39), - // (7,39): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("scoped R").WithLocation(4, 39), + // (7,39): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R Explicit1(scoped R r) => (R)r; - Diagnostic(ErrorCode.ERR_EscapeVariable, "(R)r").WithArguments("R").WithLocation(7, 39)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "(R)r").WithArguments("scoped R").WithLocation(7, 39)); } [Fact] @@ -12822,106 +15226,402 @@ static void Main() }}"; var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); comp.VerifyDiagnostics( - // (16,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D2'. - // D2 d2 = (ref int i) => ref F(); // 1 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(ref int i) => ref F()").WithArguments("i", "D2").WithLocation(16, 22), - // (17,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D3'. - // D3 d3 = (in int i) => ref F(); // 2 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(in int i) => ref F()").WithArguments("i", "D3").WithLocation(17, 22), - // (18,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D4'. - // D4 d4 = ([UnscopedRef] out int i) => { i = 0; return ref F(); }; // 3 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "([UnscopedRef] out int i) => { i = 0; return ref F(); }").WithArguments("i", "D4").WithLocation(18, 22), - // (19,22): error CS8986: The 'scoped' modifier of parameter 'r' doesn't match target 'D5'. - // D5 d5 = (R r) => ref F(); // 4 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(R r) => ref F()").WithArguments("r", "D5").WithLocation(19, 22)); + // (16,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D2'. + // D2 d2 = (ref int i) => ref F(); // 1 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(ref int i) => ref F()").WithArguments("i", "D2").WithLocation(16, 22), + // (17,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D3'. + // D3 d3 = (in int i) => ref F(); // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(in int i) => ref F()").WithArguments("i", "D3").WithLocation(17, 22), + // (18,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D4'. + // D4 d4 = ([UnscopedRef] out int i) => { i = 0; return ref F(); }; // 3 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "([UnscopedRef] out int i) => { i = 0; return ref F(); }").WithArguments("i", "D4").WithLocation(18, 22), + // (19,22): error CS8986: The 'scoped' modifier of parameter 'r' doesn't match target 'D5'. + // D5 d5 = (R r) => ref F(); // 4 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(R r) => ref F()").WithArguments("r", "D5").WithLocation(19, 22)); + } + + [Theory] + [InlineData("ref ")] + [InlineData("ref readonly")] + public void DelegateConversions_12(string refModifier) + { + var source = +$@"using System.Diagnostics.CodeAnalysis; +ref struct R {{ }} +delegate {refModifier} T D0(); +delegate {refModifier} T D1(T t); +delegate {refModifier} T D2(ref T t); +delegate {refModifier} T D3(in T t); +delegate {refModifier} T D4(out T t); +delegate {refModifier} T D5(R r); +struct S1 +{{ + private {refModifier} T F() => throw null; + public {refModifier} T F0() => ref F(); + public {refModifier} T F1(T t) => ref F(); + public {refModifier} T F2(ref T t) => ref F(); + public {refModifier} T F3(in T t) => ref F(); + public {refModifier} T F4(out T t) {{ t = default; return ref F(); }} + public {refModifier} T F5(R r) => ref F(); +}} +struct S2 +{{ + private {refModifier} T F() => throw null; + [UnscopedRef] public {refModifier} T F0() => ref F(); + [UnscopedRef] public {refModifier} T F1(T t) => ref F(); + [UnscopedRef] public {refModifier} T F2(ref T t) => ref F(); + [UnscopedRef] public {refModifier} T F3(in T t) => ref F(); + [UnscopedRef] public {refModifier} T F4(out T t) {{ t = default; return ref F(); }} + [UnscopedRef] public {refModifier} T F5(R r) => ref F(); +}} +class Program +{{ + static void F1(ref S1 s1) + {{ + D0 d0 = s1.F0; + D1 d1 = s1.F1; + D2 d2 = s1.F2; + D3 d3 = s1.F3; + D4 d4 = s1.F4; + D5 d5 = s1.F5; + }} + static void F2(ref S2 s2) + {{ + D0 d0 = s2.F0; + D1 d1 = s2.F1; + D2 d2 = s2.F2; + D3 d3 = s2.F3; + D4 d4 = s2.F4; + D5 d5 = s2.F5; + }} +}}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics(); + } + + [Fact] + public void DelegateConversions_Out() + { + var source = +@"ref struct R { } +delegate void D1(out int x, scoped out int y); +delegate void D2(out R x, scoped out R y); +class Program +{ + static void Implicit() + { + D1 d1 = (scoped out int x, out int y) => { x = 0; y = 0; }; + D2 d2 = (scoped out R x, out R y) => { x = default; y = default; }; + } + static void Explicit() + { + var d1 = (D1)((scoped out int x, out int y) => { x = 0; y = 0; }); + var d2 = (D2)((scoped out R x, out R y) => { x = default; y = default; }); + } + static void New() + { + var d1 = new D1((scoped out int x, out int y) => { x = 0; y = 0; }); + var d2 = new D2((scoped out R x, out R y) => { x = default; y = default; }); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter() + { + var source = """ + delegate R D1(scoped R r); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + D1 d1 = r => r; // 1 + D1 d1_2 = (scoped R r) => r; // 2 + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (12,22): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope + // D1 d1 = r => r; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("scoped R").WithLocation(12, 22), + // (13,35): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope + // D1 d1_2 = (scoped R r) => r; // 2 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("scoped R").WithLocation(13, 35) + ); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_OverloadResolution() + { + var source = """ + delegate R D1(scoped R r); + delegate R D2(R r); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + M2(r => r); + M2((scoped R r) => r); // 1 + M2(r => default); // 2 + } + + void M2(D1 d1) { } + void M2(D2 d2) { } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (14,28): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope + // M2((scoped R r) => r); // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("scoped R").WithLocation(14, 28), + // (15,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M2(D1)' and 'C.M2(D2)' + // M2(r => default); // 2 + Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments("C.M2(D1)", "C.M2(D2)").WithLocation(15, 9) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocations = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal("M2(r => r)", invocations[0].ToString()); + Assert.Equal("void C.M2(D2 d2)", model.GetSymbolInfo(invocations[0]).Symbol.ToTestDisplayString()); + + Assert.Equal("M2((scoped R r) => r)", invocations[1].ToString()); + Assert.Null(model.GetSymbolInfo(invocations[1]).Symbol); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_ParameterlessAnonymousMethod_Ref() + { + var source = """ + delegate void D1(scoped R r1, scoped ref R r2); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + D1 d1 = delegate { }; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var anonymousMethod = tree.GetRoot().DescendantNodes().OfType().Single(); + + Assert.Equal("delegate { }", anonymousMethod.ToString()); + var method = model.GetSymbolInfo(anonymousMethod).Symbol; + Assert.Equal("lambda expression", method.ToTestDisplayString()); + var parameters = method.GetParameters(); + Assert.Equal("scoped R ", parameters[0].ToTestDisplayString()); + Assert.Equal("scoped ref R ", parameters[1].ToTestDisplayString()); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_ParameterlessAnonymousMethod_Out() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + delegate void D1([UnscopedRef] out R r3); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + D1 d1 = delegate { }; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (13,17): error CS1688: Cannot convert anonymous method block without a parameter list to delegate type 'D1' because it has one or more out parameters + // D1 d1 = delegate { }; + Diagnostic(ErrorCode.ERR_CantConvAnonMethNoParams, "delegate { }").WithArguments("D1").WithLocation(13, 17) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var anonymousMethod = tree.GetRoot().DescendantNodes().OfType().Single(); + + Assert.Equal("delegate { }", anonymousMethod.ToString()); + var method = model.GetSymbolInfo(anonymousMethod).Symbol; + Assert.Equal("lambda expression", method.ToTestDisplayString()); + Assert.Empty(method.GetParameters()); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_MissingParameters() + { + var source = """ + delegate R D1(scoped R r); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + D1 d1 = () => new R(); + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (12,17): error CS1593: Delegate 'D1' does not take 0 arguments + // D1 d1 = () => new R(); + Diagnostic(ErrorCode.ERR_BadDelArgCount, "() => new R()").WithArguments("D1", "0").WithLocation(12, 17) + ); + } + + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_ExtraParameters() + { + var source = """ + delegate R D1(); + + public ref struct R + { + public ref int field; + } + + class C + { + void M() + { + D1 d1 = r => r; + D1 d2 = (scoped R r) => r; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (12,17): error CS1593: Delegate 'D1' does not take 1 arguments + // D1 d1 = r => r; + Diagnostic(ErrorCode.ERR_BadDelArgCount, "r => r").WithArguments("D1", "1").WithLocation(12, 17), + // (13,17): error CS1593: Delegate 'D1' does not take 1 arguments + // D1 d2 = (scoped R r) => r; + Diagnostic(ErrorCode.ERR_BadDelArgCount, "(scoped R r) => r").WithArguments("D1", "1").WithLocation(13, 17) + ); } - [Theory] - [InlineData("ref ")] - [InlineData("ref readonly")] - public void DelegateConversions_12(string refModifier) + [Fact] + public void DelegateConversions_ImplicitlyTypedParameter_NullableRewriteOfLambda() { - var source = -$@"using System.Diagnostics.CodeAnalysis; -ref struct R {{ }} -delegate {refModifier} T D0(); -delegate {refModifier} T D1(T t); -delegate {refModifier} T D2(ref T t); -delegate {refModifier} T D3(in T t); -delegate {refModifier} T D4(out T t); -delegate {refModifier} T D5(R r); -struct S1 -{{ - private {refModifier} T F() => throw null; - public {refModifier} T F0() => ref F(); - public {refModifier} T F1(T t) => ref F(); - public {refModifier} T F2(ref T t) => ref F(); - public {refModifier} T F3(in T t) => ref F(); - public {refModifier} T F4(out T t) {{ t = default; return ref F(); }} - public {refModifier} T F5(R r) => ref F(); -}} -struct S2 -{{ - private {refModifier} T F() => throw null; - [UnscopedRef] public {refModifier} T F0() => ref F(); - [UnscopedRef] public {refModifier} T F1(T t) => ref F(); - [UnscopedRef] public {refModifier} T F2(ref T t) => ref F(); - [UnscopedRef] public {refModifier} T F3(in T t) => ref F(); - [UnscopedRef] public {refModifier} T F4(out T t) {{ t = default; return ref F(); }} - [UnscopedRef] public {refModifier} T F5(R r) => ref F(); -}} -class Program -{{ - static void F1(ref S1 s1) - {{ - D0 d0 = s1.F0; - D1 d1 = s1.F1; - D2 d2 = s1.F2; - D3 d3 = s1.F3; - D4 d4 = s1.F4; - D5 d5 = s1.F5; - }} - static void F2(ref S2 s2) - {{ - D0 d0 = s2.F0; - D1 d1 = s2.F1; - D2 d2 = s2.F2; - D3 d3 = s2.F3; - D4 d4 = s2.F4; - D5 d5 = s2.F5; - }} -}}"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); - comp.VerifyDiagnostics(); + var source = """ + #nullable enable + + delegate R D(scoped R r, T t); + + public ref struct R + { + public ref int field; + } + + class C + { + static R F(D f, T t) => throw null!; + + static void M(U? u) where U : class + { + F((r1, t1) => F((r2, t2) => r2, t1), u).ToString(); + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (16,37): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope + // F((r1, t1) => F((r2, t2) => r2, t1), u).ToString(); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r2").WithArguments("scoped R").WithLocation(16, 37)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var lambdas = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal("(r1, t1) => F((r2, t2) => r2, t1)", lambdas[0].ToString()); + Assert.Equal("r1", lambdas[0].ParameterList.Parameters[0].Identifier.ToString()); + Assert.Equal("scoped R r1", model.GetDeclaredSymbol(lambdas[0].ParameterList.Parameters[0]).ToTestDisplayString()); + + Assert.Equal("t1", lambdas[0].ParameterList.Parameters[1].Identifier.ToString()); + Assert.Equal("U t1", model.GetDeclaredSymbol(lambdas[0].ParameterList.Parameters[1]).ToTestDisplayString()); + + Assert.Equal("(r2, t2) => r2", lambdas[1].ToString()); + Assert.Equal("r2", lambdas[1].ParameterList.Parameters[0].Identifier.ToString()); + Assert.Equal("scoped R r2", model.GetDeclaredSymbol(lambdas[1].ParameterList.Parameters[0]).ToTestDisplayString()); + + Assert.Equal("t2", lambdas[1].ParameterList.Parameters[1].Identifier.ToString()); + Assert.Equal("U t2", model.GetDeclaredSymbol(lambdas[1].ParameterList.Parameters[1]).ToTestDisplayString()); } [Fact] - public void DelegateConversions_Out() + public void DelegateConversions_ImplicitlyTypedParameter_NestedLambdaReinference_NestedNotReinferred() { - var source = -@"ref struct R { } -delegate void D1(out int x, scoped out int y); -delegate void D2(out R x, scoped out R y); -class Program + var source = @" +using System; +delegate R D(scoped T t); +ref struct R { } + +class C { - static void Implicit() - { - D1 d1 = (scoped out int x, out int y) => { x = 0; y = 0; }; - D2 d2 = (scoped out R x, out R y) => { x = default; y = default; }; - } - static void Explicit() - { - var d1 = (D1)((scoped out int x, out int y) => { x = 0; y = 0; }); - var d2 = (D2)((scoped out R x, out R y) => { x = default; y = default; }); - } - static void New() + public static Action Create(T t, Action a) => throw null!; + + public static void M(object? o) { - var d1 = new D1((scoped out int x, out int y) => { x = 0; y = 0; }); - var d2 = new D2((scoped out R x, out R y) => { x = default; y = default; }); + var a = Create(o, o1 => { + if (o1 == null) return; + D d = o2 => throw null!; + }); } }"; - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); + + var comp = CreateCompilation(source, options: WithNullableEnable()); + comp.VerifyDiagnostics( + // (3,17): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // delegate R D(scoped T t); + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T t").WithLocation(3, 17) + ); + + var syntaxTree = comp.SyntaxTrees[0]; + var root = syntaxTree.GetRoot(); + var model = comp.GetSemanticModel(syntaxTree); + + var lambda = root.DescendantNodes().OfType().Last(); + Assert.Equal("o2 => throw null!", lambda.ToString()); + var lambdaSymbol = model.GetSymbolInfo(lambda).Symbol; + Assert.Equal("scoped System.String o2", lambdaSymbol.GetParameters()[0].ToTestDisplayString()); } [Fact] @@ -12997,7 +15697,7 @@ static void M1(R r) { } void M2(scoped R r) { } object this[R r] => null; static void M1(scoped R r) { } // 1 - void M2(R r) { } // 2 + void M2(R r) { } // 2 object this[scoped R r] => null; // 3 }"; var comp = CreateCompilation(source); @@ -13006,7 +15706,7 @@ void M2(R r) { } // 2 // static void M1(scoped R r) { } // 1 Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "C").WithLocation(7, 17), // (8,10): error CS0111: Type 'C' already defines a member called 'M2' with the same parameter types - // void M2(R r) { } // 2 + // void M2(R r) { } // 2 Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C").WithLocation(8, 10), // (9,12): error CS0111: Type 'C' already defines a member called 'this' with the same parameter types // object this[scoped R r] => null; // 3 @@ -13256,21 +15956,21 @@ public void M3(scoped ref R r) { } // 3 }"; comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( - // (11,17): warning CS0108: 'B2.M1(R)' hides inherited member 'A.M1(R)'. Use the new keyword if hiding was intended. + // (11,17): warning CS0108: 'B2.M1(scoped R)' hides inherited member 'A.M1(R)'. Use the new keyword if hiding was intended. // public void M1(scoped R r) { } // 1 - Diagnostic(ErrorCode.WRN_NewRequired, "M1").WithArguments("B2.M1(R)", "A.M1(R)").WithLocation(11, 17), - // (12,17): warning CS0108: 'B2.M2(R)' hides inherited member 'A.M2(R)'. Use the new keyword if hiding was intended. + Diagnostic(ErrorCode.WRN_NewRequired, "M1").WithArguments("B2.M1(scoped R)", "A.M1(R)").WithLocation(11, 17), + // (12,17): warning CS0108: 'B2.M2(R)' hides inherited member 'A.M2(scoped R)'. Use the new keyword if hiding was intended. // public void M2(R r) { } // 2 - Diagnostic(ErrorCode.WRN_NewRequired, "M2").WithArguments("B2.M2(R)", "A.M2(R)").WithLocation(12, 17), - // (13,17): warning CS0108: 'B2.M3(ref R)' hides inherited member 'A.M3(ref R)'. Use the new keyword if hiding was intended. + Diagnostic(ErrorCode.WRN_NewRequired, "M2").WithArguments("B2.M2(R)", "A.M2(scoped R)").WithLocation(12, 17), + // (13,17): warning CS0108: 'B2.M3(scoped ref R)' hides inherited member 'A.M3(ref R)'. Use the new keyword if hiding was intended. // public void M3(scoped ref R r) { } // 3 - Diagnostic(ErrorCode.WRN_NewRequired, "M3").WithArguments("B2.M3(ref R)", "A.M3(ref R)").WithLocation(13, 17), - // (14,19): warning CS0108: 'B2.this[R]' hides inherited member 'A.this[R]'. Use the new keyword if hiding was intended. + Diagnostic(ErrorCode.WRN_NewRequired, "M3").WithArguments("B2.M3(scoped ref R)", "A.M3(ref R)").WithLocation(13, 17), + // (14,19): warning CS0108: 'B2.this[scoped R]' hides inherited member 'A.this[R]'. Use the new keyword if hiding was intended. // public object this[scoped R r] { get { return null; } set { } } // 4 - Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("B2.this[R]", "A.this[R]").WithLocation(14, 19), - // (15,19): warning CS0108: 'B2.this[int, R]' hides inherited member 'A.this[int, R]'. Use the new keyword if hiding was intended. + Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("B2.this[scoped R]", "A.this[R]").WithLocation(14, 19), + // (15,19): warning CS0108: 'B2.this[int, R]' hides inherited member 'A.this[int, scoped R]'. Use the new keyword if hiding was intended. // public object this[int x, R y] => null; // 5 - Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("B2.this[int, R]", "A.this[int, R]").WithLocation(15, 19)); + Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("B2.this[int, R]", "A.this[int, scoped R]").WithLocation(15, 19)); } [CombinatorialData] @@ -13724,7 +16424,7 @@ static void Main() R r2 = F2(new B()); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,23): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. // public override R F1(R r) => r; @@ -13760,7 +16460,7 @@ static void Main() R r2 = F2((scoped R x) => default); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (18,16): error CS8347: Cannot use a result of 'D2.Invoke(R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope // return d2(new R(ref i)); @@ -13788,12 +16488,12 @@ class Program }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (4,53): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (4,53): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R F1(bool b, R x, scoped R y) => b ? x : y; - Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("R").WithLocation(4, 53), - // (5,49): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("scoped R").WithLocation(4, 53), + // (5,49): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R F2(bool b, R x, scoped R y) => b ? y : x; - Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("R").WithLocation(5, 49)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("scoped R").WithLocation(5, 49)); } [Fact] @@ -13808,12 +16508,12 @@ unsafe class Program }"; var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( - // (4,53): warning CS9077: Use of variable 'R' in this context may expose referenced variables outside of their declaration scope + // (4,53): warning CS9080: Use of variable 'scoped R' in this context may expose referenced variables outside of their declaration scope // static R F1(bool b, R x, scoped R y) => b ? x : y; - Diagnostic(ErrorCode.WRN_EscapeVariable, "y").WithArguments("R").WithLocation(4, 53), - // (5,49): warning CS9077: Use of variable 'R' in this context may expose referenced variables outside of their declaration scope + Diagnostic(ErrorCode.WRN_EscapeVariable, "y").WithArguments("scoped R").WithLocation(4, 53), + // (5,49): warning CS9080: Use of variable 'scoped R' in this context may expose referenced variables outside of their declaration scope // static R F2(bool b, R x, scoped R y) => b ? y : x; - Diagnostic(ErrorCode.WRN_EscapeVariable, "y").WithArguments("R").WithLocation(5, 49)); + Diagnostic(ErrorCode.WRN_EscapeVariable, "y").WithArguments("scoped R").WithLocation(5, 49)); } [Fact] @@ -13885,21 +16585,21 @@ static void F3(ref int x3, scoped ref int y3) var comp = CreateCompilation(source); // https://github.com/dotnet/roslyn/issues/62768: Improve error message for `scoped ref` parameter returned by reference. comp.VerifyDiagnostics( - // (9,13): error CS8347: Cannot use a result of '.Invoke(R, R)' in this context because it may expose variables referenced by parameter '0' outside of their declaration scope + // (9,13): error CS8347: Cannot use a result of '.Invoke(R, scoped R)' in this context because it may expose variables referenced by parameter 'arg1' outside of their declaration scope // z = f(y1, x1); // 1 - Diagnostic(ErrorCode.ERR_EscapeCall, "f(y1, x1)").WithArguments(".Invoke(R, R)", "0").WithLocation(9, 13), - // (9,15): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + Diagnostic(ErrorCode.ERR_EscapeCall, "f(y1, x1)").WithArguments(".Invoke(R, scoped R)", "arg1").WithLocation(9, 13), + // (9,15): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // z = f(y1, x1); // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "y1").WithArguments("R").WithLocation(9, 15)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "y1").WithArguments("scoped R").WithLocation(9, 15)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var decls = tree.GetRoot().DescendantNodes().OfType().Where(v => v.Identifier.Text == "f").ToArray(); var delegateInvokeMethods = decls.Select(d => ((ILocalSymbol)model.GetDeclaredSymbol(d)).Type.GetSymbol().DelegateInvokeMethod).ToArray(); - VerifyParameterSymbol(delegateInvokeMethods[0].Parameters[0], "R", RefKind.None, DeclarationScope.Unscoped); - VerifyParameterSymbol(delegateInvokeMethods[0].Parameters[1], "scoped R", RefKind.None, DeclarationScope.ValueScoped); - VerifyParameterSymbol(delegateInvokeMethods[1].Parameters[1], "scoped ref System.Int32", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(delegateInvokeMethods[0].Parameters[0], "R arg1", RefKind.None, DeclarationScope.Unscoped); + VerifyParameterSymbol(delegateInvokeMethods[0].Parameters[1], "scoped R arg2", RefKind.None, DeclarationScope.ValueScoped); + VerifyParameterSymbol(delegateInvokeMethods[1].Parameters[1], "scoped ref System.Int32 arg2", RefKind.Ref, DeclarationScope.RefScoped); } [Fact] @@ -13928,12 +16628,12 @@ static void Main() }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (17,18): error CS0121: The call is ambiguous between the following methods or properties: 'E1.F1(object, R)' and 'E2.F1(object, R)' + // (17,18): error CS0121: The call is ambiguous between the following methods or properties: 'E1.F1(object, R)' and 'E2.F1(object, scoped R)' // var d1 = o.F1; - Diagnostic(ErrorCode.ERR_AmbigCall, "o.F1").WithArguments("E1.F1(object, R)", "E2.F1(object, R)").WithLocation(17, 18), - // (18,18): error CS0121: The call is ambiguous between the following methods or properties: 'E1.F2(object, ref R)' and 'E2.F2(object, ref R)' + Diagnostic(ErrorCode.ERR_AmbigCall, "o.F1").WithArguments("E1.F1(object, R)", "E2.F1(object, scoped R)").WithLocation(17, 18), + // (18,18): error CS0121: The call is ambiguous between the following methods or properties: 'E1.F2(object, ref R)' and 'E2.F2(object, scoped ref R)' // var d2 = o.F2; - Diagnostic(ErrorCode.ERR_AmbigCall, "o.F2").WithArguments("E1.F2(object, ref R)", "E2.F2(object, ref R)").WithLocation(18, 18)); + Diagnostic(ErrorCode.ERR_AmbigCall, "o.F2").WithArguments("E1.F2(object, ref R)", "E2.F2(object, scoped ref R)").WithLocation(18, 18)); } [Fact] @@ -14196,7 +16896,7 @@ static void Main() Console.WriteLine(x); } }"; - var comp = CreateCompilation(source, options: TestOptions.DebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped); verifier.VerifyIL("Program.Main", source: source, @@ -14248,9 +16948,9 @@ class Program }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (5,43): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (5,43): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static R F1(scoped R r1) => r1; // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("R").WithLocation(5, 43)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("scoped R").WithLocation(5, 43)); } [Fact] @@ -14546,9 +17246,9 @@ static R F2(ref R r2) }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (11,36): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (11,36): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // scoped ref R l1 = ref r1; // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("R").WithLocation(11, 36)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("scoped R").WithLocation(11, 36)); } [Fact] @@ -14584,9 +17284,9 @@ static ref R F3(scoped ref R r3) // (7,20): error CS8157: Cannot return 'l0' by reference because it was initialized to a value that cannot be returned by reference // return ref l0; // 1 Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l0").WithArguments("l0").WithLocation(7, 20), - // (11,36): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (11,36): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // scoped ref R l1 = ref r1; // 2 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("R").WithLocation(11, 36), + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("scoped R").WithLocation(11, 36), // (12,20): error CS8157: Cannot return 'l1' by reference because it was initialized to a value that cannot be returned by reference // return ref l1; // 3 Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l1").WithArguments("l1").WithLocation(12, 20), @@ -14651,7 +17351,7 @@ public void ReturnRefField() public ref T GetRef() => ref F; public ref readonly T GetRefReadonly() => ref F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -14666,11 +17366,11 @@ public void ReturnRefReadonlyField() public ref T GetRef() => ref F; // 1 public ref readonly T GetRefReadonly() => ref F; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (5,34): error CS8333: Cannot return field 'R.F' by writable reference because it is a readonly variable + // (5,34): error CS8333: Cannot return field 'F' by writable reference because it is a readonly variable // public ref T GetRef() => ref F; // 1 - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "R.F").WithLocation(5, 34)); + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "F").WithLocation(5, 34)); } [Fact] @@ -14744,7 +17444,7 @@ static void M(ref RS1 rs, ref RS2 rs2) rs2 = new RS2 { I2 = ref rs.I1 }; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (16,34): error CS9078: Cannot return by reference a member of parameter 'rs' through a ref parameter; it can only be returned in a return statement // rs2 = new RS2 { I2 = ref rs.I1 }; @@ -14773,7 +17473,7 @@ static unsafe void M(ref RS1 rs, ref RS2 rs2) rs2 = new RS2 { I2 = ref rs.I1 }; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (16,34): warning CS9095: This returns by reference a member of parameter 'rs' through a ref parameter; but it can only safely be returned in a return statement // rs2 = new RS2 { I2 = ref rs.I1 }; @@ -14851,7 +17551,7 @@ class Program static T F6(scoped out R r) {{ r = default; return r.F; }} static T F7(scoped in R r) => r.F; }}"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -14877,11 +17577,11 @@ class Program static {refOrRefReadonly} T F6(scoped out R r) {{ r = default; return ref r.F; }} static {refOrRefReadonly} T F7(scoped in R r) => ref r.F; }}"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (12,55): error CS8352: Cannot use variable 'R' in this context because it may expose referenced variables outside of their declaration scope + // (12,55): error CS8352: Cannot use variable 'scoped R' in this context because it may expose referenced variables outside of their declaration scope // static ref T F4(scoped R r) => ref r.F; // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r.F").WithArguments("R").WithLocation(12, 55)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r.F").WithArguments("scoped R").WithLocation(12, 55)); } [Fact] @@ -14907,7 +17607,7 @@ static ref readonly T F2(scoped ref T t) return ref r2.F; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,20): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // return ref r1.F; // 1 @@ -14942,7 +17642,7 @@ static ref readonly T F2(scoped in T t) return ref r2.F; // 2 } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,20): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // return ref r1.F; // 1 @@ -14968,7 +17668,7 @@ class Program static R F3(scoped ref T t) => new R(ref t); // 2 static R F4(scoped out T t, T tValue) { t = tValue; return new R(ref t); } // 3 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,63): error CS8347: Cannot use a result of 'R.R(ref T)' in this context because it may expose variables referenced by parameter 't' outside of their declaration scope // static R F2(out T t, T tValue) { t = tValue; return new R(ref t); } // 1 @@ -15011,11 +17711,11 @@ class Program static R0 F2(ref R1 r2) => r2.F1; static R0 F3(scoped ref R1 r3) => r3.F1; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (14,44): error CS8352: Cannot use variable 'R1' in this context because it may expose referenced variables outside of their declaration scope + // (14,44): error CS8352: Cannot use variable 'scoped R1' in this context because it may expose referenced variables outside of their declaration scope // static R0 F1(scoped R1 r1) => r1.F1; // 1 - Diagnostic(ErrorCode.ERR_EscapeVariable, "r1.F1").WithArguments("R1").WithLocation(14, 44)); + Diagnostic(ErrorCode.ERR_EscapeVariable, "r1.F1").WithArguments("scoped R1").WithLocation(14, 44)); } [Fact] @@ -15039,7 +17739,7 @@ class Program static ref R0 F2(ref R1 r2) => ref r2.F1; static ref R0 F3(scoped ref R1 r3) => ref r3.F1; // 3 }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,45): error CS8167: Cannot return by reference a member of parameter 'r0' because it is not a ref or out parameter // static ref R0 F0(R1 r0) => ref r0.F1; // 1 @@ -15077,7 +17777,7 @@ static ref T F2() return ref F0(new R(ref t)); // error } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (19,20): error CS8347: Cannot use a result of 'Program.F0(R)' in this context because it may expose variables referenced by parameter 'r0' outside of their declaration scope // return ref F0(new R(ref t)); // error @@ -15121,7 +17821,7 @@ static R F4(C c, R y4) return c[new R(ref i4), y4]; // 2 } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (16,16): error CS8347: Cannot use a result of 'C.this[R, R]' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope // return this[x2, new R(ref i2)]; // 1 @@ -15174,7 +17874,7 @@ static R F4(C c, in int y4) return c[x4, y4]; // 2 } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (16,16): error CS8347: Cannot use a result of 'C.this[in int, in int]' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope // return this[x2, y2]; // 1 @@ -15221,7 +17921,7 @@ static ref int F4(C c, R y4) return ref c[new R(ref i4), y4]; // 2 } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (16,20): error CS8347: Cannot use a result of 'C.this[R, R]' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope // return ref this[x2, new R(ref i2)]; // 1 @@ -15360,7 +18060,7 @@ static T F8() return r8.F; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (29,9): error CS8374: Cannot ref-assign 't' to 'F' because 't' has a narrower escape scope than 'F'. // r3.F = ref t; @@ -15438,7 +18138,7 @@ static T F8(T t) return r8.F; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (26,9): error CS8374: Cannot ref-assign 't' to 'F' because 't' has a narrower escape scope than 'F'. // r3.F = ref t; @@ -15523,7 +18223,7 @@ static R F8() return r8; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (16,16): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // return r1; @@ -15618,7 +18318,7 @@ static R F8(T t) return r8; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (15,16): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // return r1; @@ -15741,7 +18441,7 @@ public R(long unused) M(this); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (11,16): error CS8347: Cannot use a result of 'R.R(ref T)' in this context because it may expose variables referenced by parameter 't' outside of their declaration scope // this = new R(ref t1); @@ -15797,7 +18497,7 @@ static void M(R r) { } M(this); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (10,16): error CS8347: Cannot use a result of 'R.R(ref T)' in this context because it may expose variables referenced by parameter 't' outside of their declaration scope // this = new R(ref t1); @@ -15839,7 +18539,7 @@ static void M(R r) { } M(this); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (13,16): error CS8347: Cannot use a result of 'R.R(ref T)' in this context because it may expose variables referenced by parameter 't' outside of their declaration scope // this = new R(ref t2); @@ -15868,7 +18568,7 @@ static void M(R r) { } M(this); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,19): warning CS0169: The field 'R.F' is never used // private ref T F; @@ -15880,9 +18580,9 @@ static void M(R r) { } public void Scoped_Cycle() { var source = -@"interface I +@"interface I { - void M(T s); + void M(T s); } class C : I @@ -15894,9 +18594,9 @@ void I.M(scoped T? s) {} // (6,11): error CS0535: 'C' does not implement interface member 'I.M(T)' // class C : I Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I").WithArguments("C", "I.M(T)").WithLocation(6, 11), - // (8,12): error CS0539: 'C.M(T?)' in explicit interface declaration is not found among members of the interface that can be implemented + // (8,12): error CS0539: 'C.M(scoped T?)' in explicit interface declaration is not found among members of the interface that can be implemented // void I.M(scoped T? s) {} - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M").WithArguments("C.M(T?)").WithLocation(8, 12), + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "M").WithArguments("C.M(scoped T?)").WithLocation(8, 12), // (8,17): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // void I.M(scoped T? s) {} Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T? s").WithLocation(8, 17), @@ -15928,7 +18628,7 @@ static T F() return r.F; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (12,13): error CS8374: Cannot ref-assign 't' to 'F' because 't' has a narrower escape scope than 'F'. // r.F = ref t; @@ -15950,7 +18650,7 @@ public R(Span s) { } class Program { static void F(out R r) - { + { Span s1 = stackalloc int[10]; R r1 = new R(s1); r1.F(out r); @@ -15979,13 +18679,13 @@ public void InstanceMethodWithOutVar_02() class Program { static void F(out R r) - { + { int i = 0; R r1 = new R(ref i); r1.F(out r); } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,9): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope // r1.F(out r); @@ -16050,7 +18750,7 @@ private void M2(ref int i) _i = ref i; } }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,9): error CS9079: Cannot ref-assign 'i' to '_i' because 'i' can only escape the current method through a return statement. // _i = ref i; @@ -16061,16 +18761,6 @@ private void M2(ref int i) [Fact] public void RefToLocalFromInstanceMethod_02() { - var sourceA = -@"namespace System -{ - public ref struct Span - { - unsafe public Span(void* ptr, int length) { } - public ref T this[int index] => throw null; - public static implicit operator Span(T[] a) => throw null; - } -}"; var sourceB = @"using System; ref struct S @@ -16086,7 +18776,7 @@ private void M2(ref int i) _i = ref i; } }"; - var comp = CreateCompilation(new[] { sourceA, sourceB }, options: TestOptions.UnsafeReleaseDll, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceB, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (12,9): error CS9079: Cannot ref-assign 'i' to '_i' because 'i' can only escape the current method through a return statement. // _i = ref i; @@ -16099,7 +18789,7 @@ public void RefStructProperty_01() var source = @"ref struct R { - public R(ref T t) { } + public R(ref T t) { } } class C { @@ -16255,7 +18945,7 @@ ref struct S { } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,26): error CS8170: Struct members cannot return 'this' or other instance members by reference // ref int Prop1 => ref field; // 1 @@ -16283,14 +18973,14 @@ ref struct R public ref int field; } "; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (7,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // public ref int field; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(7, 12) ); - comp = CreateCompilation(source, parseOptions: TestOptions.Regular11, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -16311,9 +19001,9 @@ public ref struct R R r3 = default; _ = r3 with { field = ref x }; // 3 "; - var lib = CreateCompilation(lib_cs, parseOptions: TestOptions.Regular11, runtimeFeature: RuntimeFlag.ByRefFields); + var lib = CreateCompilation(lib_cs, parseOptions: TestOptions.Regular11, targetFramework: TargetFramework.Net70); - var comp = CreateCompilation(source, references: new[] { lib.EmitToImageReference() }, parseOptions: TestOptions.Regular10, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, references: new[] { lib.EmitToImageReference() }, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,20): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. // var r1 = new R() { field = ref x }; // 1 @@ -16372,7 +19062,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); verifier.VerifyIL("C.Main", @@ -16418,7 +19108,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,31): error CS0165: Use of unassigned local variable 'x' // var r = new R() { field = ref x }; @@ -16443,7 +19133,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,31): error CS8173: The expression must be of type 'int' because it is being assigned by reference // var r = new R() { field = ref x }; @@ -16473,7 +19163,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,31): error CS8173: The expression must be of type 'S2' because it is being assigned by reference // var r = new R() { field = ref x }; @@ -16497,7 +19187,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,19): error CS1914: Static field or property 'R.field' cannot be assigned in an object initializer // var r = new R() { field = ref x }; @@ -16519,7 +19209,7 @@ ref struct R public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (2,31): error CS8199: The syntax 'var (...)' as an lvalue is reserved. // var r = new R() { field = ref var() }; @@ -16542,7 +19232,7 @@ ref struct R public readonly ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,19): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) // var r = new R() { field = ref x }; @@ -16562,7 +19252,7 @@ ref struct R public ref readonly int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -16586,11 +19276,11 @@ ref struct R public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (8,39): error CS8331: Cannot assign to method 'C.Value()' or use it as the right hand side of a ref assignment because it is a readonly variable + // (8,39): error CS8331: Cannot assign to method 'Value' or use it as the right hand side of a ref assignment because it is a readonly variable // var r = new R() { field = ref Value() }; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "Value()").WithArguments("method", "C.Value()").WithLocation(8, 39) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "Value()").WithArguments("method", "Value").WithLocation(8, 39) ); } @@ -16619,11 +19309,11 @@ ref struct R2 "; // Diagnostic is missing parameter name // Tracked by https://github.com/dotnet/roslyn/issues/62096 - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( - // (7,31): error CS8331: Cannot assign to variable 'in int' or use it as the right hand side of a ref assignment because it is a readonly variable + // (7,31): error CS8331: Cannot assign to variable 'i' or use it as the right hand side of a ref assignment because it is a readonly variable // _ = new R2 { _f = ref i }; // 1 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "i").WithArguments("variable", "in int").WithLocation(7, 31) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "i").WithArguments("variable", "i").WithLocation(7, 31) ); } @@ -16757,7 +19447,7 @@ ref struct R public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,39): error CS1510: A ref or out value must be an assignable variable // var r = new R() { field = ref Value() }; @@ -16784,7 +19474,7 @@ ref struct R public ref readonly int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -16805,7 +19495,7 @@ interface I public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (12,20): error CS0525: Interfaces cannot contain instance fields // public ref int field; @@ -17124,7 +19814,7 @@ ref struct Item public ref int field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); verifier.VerifyIL("C.Main", @" @@ -17176,7 +19866,7 @@ ref struct R public ref S field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,33): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. // _ = new R { field = ref x2 }; // 1 @@ -17209,7 +19899,7 @@ ref struct R public ref S field; } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (2,5): warning CS0219: The variable 'x' is assigned but its value is never used // int x = 42; @@ -17267,7 +19957,7 @@ ref struct R public ref T F; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,34): error CS8173: The expression must be of type 'dynamic' because it is being assigned by reference // var r = new R { F = ref i }; // 1 @@ -17299,10 +19989,7 @@ ref struct R } "; var references = TargetFrameworkUtil.GetReferences(TargetFramework.NetCoreAppAndCSharp, additionalReferences: null); - // Note: we use skipExtraValidation so that nobody pulls - // on the compilation or its references before we set the RuntimeSupportsByRefFields flag. - var comp = CreateEmptyCompilation(source, references: CopyWithoutSharingCachedSymbols(references), options: TestOptions.DebugExe, skipExtraValidation: true); - comp.Assembly.RuntimeSupportsByRefFields = true; + var comp = CreateCompilation(source, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("4242")); verifier.VerifyIL("C.Main", """ { @@ -17424,9 +20111,9 @@ ref struct R public ref T F; } "; - var comp = CreateCompilation(source, options: TestOptions.DebugExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("4242")); verifier.VerifyIL("C.Main", """ { @@ -17583,7 +20270,7 @@ public ref struct R public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return r; // 1 @@ -17645,7 +20332,7 @@ public ref struct R public R(ref int i) { } } "; - comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return r; // 1 @@ -17716,7 +20403,7 @@ ref struct Item public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,17): error CS0136: A local or parameter named 'r' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // var r = new Container { item = { field = ref x } }; // 1 @@ -17756,7 +20443,7 @@ public override string ToString() } } "; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); verifier.VerifyIL("C.Main", @@ -17832,7 +20519,7 @@ public ref struct R public ref int field; } "; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return r; // 1 @@ -17857,7 +20544,7 @@ public void RefScoped() { ref scoped R field; }"; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (3,9): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // ref scoped R field; @@ -17921,97 +20608,97 @@ public void RefScoped() source = @"ref struct R { - void M(ref scoped R parameter) - { + void M(ref scoped R parameter) + { } }"; comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,16): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // void M(ref scoped R parameter) + // void M(ref scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(3, 16), // (3,25): error CS1003: Syntax error, ',' expected - // void M(ref scoped R parameter) + // void M(ref scoped R parameter) Diagnostic(ErrorCode.ERR_SyntaxError, "parameter").WithArguments(",").WithLocation(3, 25), // (3,25): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) - // void M(ref scoped R parameter) + // void M(ref scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(3, 25), // (3,34): error CS1001: Identifier expected - // void M(ref scoped R parameter) + // void M(ref scoped R parameter) Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(3, 34) ); source = @"ref struct R { - void M(in scoped R parameter) - { + void M(in scoped R parameter) + { } }"; comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,15): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // void M(in scoped R parameter) + // void M(in scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(3, 15), // (3,24): error CS1003: Syntax error, ',' expected - // void M(in scoped R parameter) + // void M(in scoped R parameter) Diagnostic(ErrorCode.ERR_SyntaxError, "parameter").WithArguments(",").WithLocation(3, 24), // (3,24): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) - // void M(in scoped R parameter) + // void M(in scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(3, 24), // (3,33): error CS1001: Identifier expected - // void M(in scoped R parameter) + // void M(in scoped R parameter) Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(3, 33) ); source = @"ref struct R { - void M(out scoped R parameter) - { + void M(out scoped R parameter) + { } }"; comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,10): error CS0177: The out parameter 'R' must be assigned to before control leaves the current method - // void M(out scoped R parameter) + // void M(out scoped R parameter) Diagnostic(ErrorCode.ERR_ParamUnassigned, "M").WithArguments("R").WithLocation(3, 10), // (3,16): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // void M(out scoped R parameter) + // void M(out scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(3, 16), // (3,25): error CS1003: Syntax error, ',' expected - // void M(out scoped R parameter) + // void M(out scoped R parameter) Diagnostic(ErrorCode.ERR_SyntaxError, "parameter").WithArguments(",").WithLocation(3, 25), // (3,25): error CS0246: The type or namespace name 'parameter' could not be found (are you missing a using directive or an assembly reference?) - // void M(out scoped R parameter) + // void M(out scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "parameter").WithArguments("parameter").WithLocation(3, 25), // (3,34): error CS1001: Identifier expected - // void M(out scoped R parameter) + // void M(out scoped R parameter) Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(3, 34) ); source = @"ref struct R { - void M(ref scoped scoped R parameter) - { + void M(ref scoped scoped R parameter) + { } }"; comp = CreateCompilation(source); comp.VerifyDiagnostics( // (3,16): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) - // void M(ref scoped scoped R parameter) + // void M(ref scoped scoped R parameter) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(3, 16), // (3,30): error CS1003: Syntax error, ',' expected - // void M(ref scoped scoped R parameter) + // void M(ref scoped scoped R parameter) Diagnostic(ErrorCode.ERR_SyntaxError, "R").WithArguments(",").WithLocation(3, 30) ); source = @"ref struct R { - void M() - { + void M() + { ref scoped R local; } }"; @@ -18040,8 +20727,8 @@ void M() source = @"ref struct R { - void M() - { + void M() + { scoped ref scoped R local; } }"; @@ -18520,7 +21207,7 @@ R this[int i] set { value.F = ref this; } // 2 } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,15): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. // set { value.F = ref this; } // 1 @@ -18556,7 +21243,7 @@ R this[int i] init { value.F = ref this; } // 2 } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition, IsExternalInitTypeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,16): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. // init { value.F = ref this; } // 1 @@ -18729,14 +21416,14 @@ struct B r.F = ref this; // 2 } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (10,9): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. // r.F = ref this; // 1 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.F = ref this").WithArguments("F", "this").WithLocation(10, 9), - // (15,6): error CS9063: UnscopedRefAttribute cannot be applied to this item because it is unscoped by default. + // (15,6): error CS0592: Attribute 'UnscopedRef' is not valid on this declaration type. It is only valid on 'method, property, indexer, parameter' declarations. // [UnscopedRef] - Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(15, 6), + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "UnscopedRef").WithArguments("UnscopedRef", "method, property, indexer, parameter").WithLocation(15, 6), // (18,9): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. // r.F = ref this; // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.F = ref this").WithArguments("F", "this").WithLocation(18, 9)); @@ -18843,7 +21530,7 @@ public ref T F2A(scoped ref R r2) return ref r2.F; } }"; - var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); @@ -18863,7 +21550,7 @@ ref int F2B() return ref F2A(ref r); // 2 } }"; - comp = CreateCompilation(sourceB1, references: new[] { refA }); + comp = CreateCompilation(sourceB1, references: new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,20): error CS8347: Cannot use a result of 'A.F1A(ref R)' in this context because it may expose variables referenced by parameter 'r1' outside of their declaration scope // return ref F1A(ref r); // 1 @@ -18871,9 +21558,9 @@ ref int F2B() // (7,28): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return ref F1A(ref r); // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(7, 28), - // (13,20): error CS8347: Cannot use a result of 'A.F2A(ref R)' in this context because it may expose variables referenced by parameter 'r2' outside of their declaration scope + // (13,20): error CS8347: Cannot use a result of 'A.F2A(scoped ref R)' in this context because it may expose variables referenced by parameter 'r2' outside of their declaration scope // return ref F2A(ref r); // 2 - Diagnostic(ErrorCode.ERR_EscapeCall, "F2A(ref r)").WithArguments("A.F2A(ref R)", "r2").WithLocation(13, 20), + Diagnostic(ErrorCode.ERR_EscapeCall, "F2A(ref r)").WithArguments("A.F2A(scoped ref R)", "r2").WithLocation(13, 20), // (13,28): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope // return ref F2A(ref r); // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(13, 28) @@ -18910,7 +21597,7 @@ ref int F2C(ref int i) return ref y; } }"; - comp = CreateCompilation(sourceB2, references: new[] { refA }); + comp = CreateCompilation(sourceB2, references: new[] { refA }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (6,20): error CS8347: Cannot use a result of 'A.F1A(ref R)' in this context because it may expose variables referenced by parameter 'r1' outside of their declaration scope // return ref F1A(ref r); // 1, 2 @@ -18953,7 +21640,7 @@ public ref T F4([UnscopedRef] scoped R t4) // 4 throw null; } }"; - var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,22): error CS9063: UnscopedRefAttribute cannot be applied to this item because it is unscoped by default. // public ref T F1([UnscopedRef] scoped ref R r1) // 1 @@ -19338,7 +22025,7 @@ ref int F4B() return ref F4A(r); // 3 } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (17,23): error CS9063: UnscopedRefAttribute cannot be applied to this item because it is unscoped by default. // public ref T F3A([UnscopedRef] R r3) @@ -19388,7 +22075,7 @@ static void F2([UnscopedRef] ref R r2) { } static void F3([UnscopedRef] in R r3) { } static void F4([UnscopedRef] out R r4) { r4 = default; } }"; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (8,24): error CS9063: UnscopedRefAttribute cannot be applied to this item because it is unscoped by default. // static void F1([UnscopedRef] R r1) { } @@ -19843,7 +22530,7 @@ static ref int F2([UnscopedRef(F1())] out int i) } [Fact] - public void UnscopedRefAttribute_ScopeRefAttribute() + public void UnscopedRefAttribute_ScopeRefAttribute_Out() { var sourceA = @".assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } @@ -19880,14 +22567,14 @@ .param [1] .method public static int32& UnscopedRefOnly([out] int32& i) { .param [1] - .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) ldnull throw } .method public static int32& ScopedRefAndUnscopedRef([out] int32& i) { .param [1] - .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) .param [1] .custom instance void System.Runtime.CompilerServices.ScopedRefAttribute::.ctor() = ( 01 00 00 00 ) ldnull @@ -19940,6 +22627,104 @@ static ref int F4() VerifyParameterSymbol(typeA.GetMethod("ScopedRefAndUnscopedRef").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.Unscoped); } + [WorkItem(64778, "https://github.com/dotnet/roslyn/issues/64778")] + [Fact] + public void UnscopedRefAttribute_ScopeRefAttribute_RefToRefStruct() + { + var sourceA = +@".assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } +.assembly '<>' { } +.module '<>.dll' +.custom instance void System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = { int32(11) } +.class private System.Runtime.CompilerServices.RefSafetyRulesAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor(int32 version) cil managed { ret } + .field public int32 Version +} +.class private System.Diagnostics.CodeAnalysis.UnscopedRefAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class private System.Runtime.CompilerServices.ScopedRefAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public sealed R extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field public int32& F +} +.class public A +{ + .method public static void NoAttributes(valuetype R& x, valuetype R& y) + { + ldnull + throw + } + .method public static void ScopedRefOnly(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ScopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public static void UnscopedRefOnly(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public static void ScopedRefAndUnscopedRef(valuetype R& x, valuetype R& y) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.ScopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } +} +"; + var refA = CompileIL(sourceA, prependDefaultHeader: false); + + var sourceB = +@"class Program +{ + static void F1(ref R x, ref R y) + { + A.NoAttributes(ref x, ref y); + } + static void F2(ref R x, ref R y) + { + A.ScopedRefOnly(ref x, ref y); + } + static void F3(ref R x, ref R y) + { + A.UnscopedRefOnly(ref x, ref y); // 1 + } + static void F4(ref R x, ref R y) + { + A.ScopedRefAndUnscopedRef(ref x, ref y); // 2 + } +}"; + // https://github.com/dotnet/roslyn/issues/64778: No error reported for F3 because + // [UnscopedRef] ref R y is currently treated as ref R y from metadata. + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics( + // (17,11): error CS0570: 'A.ScopedRefAndUnscopedRef(ref R, ref R)' is not supported by the language + // A.ScopedRefAndUnscopedRef(ref x, ref y); // 2 + Diagnostic(ErrorCode.ERR_BindToBogus, "ScopedRefAndUnscopedRef").WithArguments("A.ScopedRefAndUnscopedRef(ref R, ref R)").WithLocation(17, 11)); + + // https://github.com/dotnet/roslyn/issues/64778: + // [UnscopedRef] ref R y is currently treated as ref R y from metadata. + var typeA = comp.GetMember("A"); + VerifyParameterSymbol(typeA.GetMethod("NoAttributes").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefOnly").Parameters[0], "scoped ref R x", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(typeA.GetMethod("UnscopedRefOnly").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefAndUnscopedRef").Parameters[0], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); + } + [Fact] [WorkItem(63529, "https://github.com/dotnet/roslyn/issues/63529")] public void CallUnscopedRefMethodFromScopedOne() @@ -19959,7 +22744,7 @@ void Test() Span GetSpan() => default; } "; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseDll); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net50, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( ); } @@ -20078,7 +22863,7 @@ static void Main() static void verifyParameter((NamedTypeSymbol, LambdaSymbol) delegateTypeAndLambda, int parameterIndex, string expectedDisplayType, string expectedDisplayName, RefKind expectedRefKind, DeclarationScope expectedScope) { var (delegateType, lambda) = delegateTypeAndLambda; - VerifyParameterSymbol(delegateType.DelegateInvokeMethod.Parameters[parameterIndex], expectedDisplayType, expectedRefKind, expectedScope); + VerifyParameterSymbol(delegateType.DelegateInvokeMethod.Parameters[parameterIndex], $"{expectedDisplayType} arg", expectedRefKind, expectedScope); VerifyParameterSymbol(lambda.Parameters[parameterIndex], $"{expectedDisplayType} {expectedDisplayName}", expectedRefKind, expectedScope); } @@ -20119,11 +22904,11 @@ unsafe public class A bool useUpdatedRules = languageVersionA == LanguageVersion.CSharp11; - VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F1").Parameters[0], "out System.Int32", RefKind.Out, useUpdatedRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); + VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F1").Parameters[0], "out modreq(System.Runtime.InteropServices.OutAttribute) System.Int32", RefKind.Out, useUpdatedRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F2").Parameters[0], "R", RefKind.None, DeclarationScope.Unscoped); VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F3").Parameters[0], "ref R", RefKind.Ref, DeclarationScope.Unscoped); - VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F4").Parameters[0], "in R", RefKind.In, DeclarationScope.Unscoped); - VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F5").Parameters[0], "out R", RefKind.Out, useUpdatedRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); + VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F4").Parameters[0], "in modreq(System.Runtime.InteropServices.InAttribute) R", RefKind.In, DeclarationScope.Unscoped); + VerifyParameterSymbol(getFunctionPointerMethod(comp, "A.F5").Parameters[0], "out modreq(System.Runtime.InteropServices.OutAttribute) R", RefKind.Out, useUpdatedRules ? DeclarationScope.RefScoped : DeclarationScope.Unscoped); static MethodSymbol getFunctionPointerMethod(CSharpCompilation comp, string qualifiedName) => ((FunctionPointerTypeSymbol)comp.GetMember(qualifiedName).Type).Signature; @@ -20617,6 +23402,7 @@ .class public A Assert.True(method.ContainingModule.UseUpdatedEscapeRules); } + [WorkItem(63691, "https://github.com/dotnet/roslyn/issues/63691")] [Theory] [CombinatorialData] public void DetectUpdatedEscapeRulesFromCorlib( @@ -20667,12 +23453,13 @@ static void Main() { } Assert.Equal(assemblyIdentity, module.ReferencedAssemblies.Single()); Assert.Equal(assemblyIdentity, module.ContainingAssembly.CorLibrary.Identity); - Assert.Equal(languageVersion == LanguageVersion.CSharp11 || majorVersion == 7, module.UseUpdatedEscapeRules); + Assert.Equal(languageVersion == LanguageVersion.CSharp11, module.UseUpdatedEscapeRules); module = comp.GetMember("System.Object").ContainingModule; - Assert.Equal(majorVersion == 7, module.UseUpdatedEscapeRules); + Assert.False(module.UseUpdatedEscapeRules); } + [WorkItem(63691, "https://github.com/dotnet/roslyn/issues/63691")] [Theory] [CombinatorialData] public void DetectUpdatedEscapeRulesFromCorlib_Retargeting( @@ -20735,11 +23522,12 @@ static void Main() { } Assert.Equal(languageVersion == LanguageVersion.CSharp11, module.UseUpdatedEscapeRules); module = module.ContainingAssembly.CorLibrary.Modules[0]; - Assert.Equal(higherVersion == 7, module.UseUpdatedEscapeRules); + Assert.False(module.UseUpdatedEscapeRules); } + [WorkItem(63691, "https://github.com/dotnet/roslyn/issues/63691")] [Theory] - [InlineData("System.Runtime", 7, 0, true)] + [InlineData("System.Runtime", 7, 0, false)] [InlineData("System.Runtime", 7, 1, false)] [InlineData("System.Runtime", 8, 0, false)] [InlineData("mscorlib", 7, 0, false)] @@ -21025,7 +23813,7 @@ static RefByteContainer M42(scoped ref ByteContainer bc) } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (42,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement // rbc = bc.ByteRef; // 1 @@ -21107,11 +23895,26 @@ static void verify(CSharpCompilation comp) foreach (var decl in decls) { var type = ((VariableDeclarationSyntax)decl.Parent).Type; - Assert.Null(model.GetTypeInfo(type).Type); - Assert.Equal("R", model.GetSymbolInfo(type.SkipScoped(out _).SkipRef(out _)).Symbol.ToTestDisplayString()); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } } } @@ -21182,11 +23985,26 @@ static void verify(CSharpCompilation comp) foreach (var decl in decls) { var type = ((VariableDeclarationSyntax)decl.Parent).Type; - Assert.Null(model.GetTypeInfo(type).Type); - Assert.Equal("R", model.GetSymbolInfo(type.SkipScoped(out _).SkipRef(out _)).Symbol.ToTestDisplayString()); + Assert.True(SyntaxFacts.IsInTypeOnlyContext(type)); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _))); Assert.True(SyntaxFacts.IsInTypeOnlyContext(type.SkipScoped(out _).SkipRef(out _))); + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } } } @@ -21437,7 +24255,13 @@ static void verifyModel(CSharpCompilation comp) foreach (var decl in decls) { - var type = ((VariableDeclarationSyntax)decl.Parent).Type.SkipScoped(out _).SkipRef(out _); + var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } @@ -21491,7 +24315,13 @@ static void verifyModel(CSharpCompilation comp) foreach (var decl in decls) { - var type = ((VariableDeclarationSyntax)decl.Parent).Type.SkipScoped(out _).SkipRef(out _); + var type = ((VariableDeclarationSyntax)decl.Parent).Type; + + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = type.SkipScoped(out _); + Assert.Equal("R", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); Assert.Equal("R", model.GetTypeInfo(type).Type.ToTestDisplayString()); } @@ -21912,7 +24742,7 @@ void lambdaContainer() } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -21991,7 +24821,7 @@ ref struct S2 { } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (7,5): error CS8374: Cannot ref-assign 'p1.field' to 'refField' because 'p1.field' has a narrower escape scope than 'refField'. // p2.refField = ref p1.field; // 1 @@ -22107,7 +24937,7 @@ ref struct S2 { } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugExe); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.UnsafeDebugExe); comp.VerifyDiagnostics( // (7,5): warning CS9085: This ref-assigns 'p1.field' to 'refField' but 'p1.field' has a narrower escape scope than 'refField'. // p2.refField = ref p1.field; // 1 @@ -22187,7 +25017,7 @@ void M5(out S2 p) { } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,9): error CS8374: Cannot ref-assign 'this.field' to 'refField' because 'this.field' has a narrower escape scope than 'refField'. // p.refField = ref this.field; // 1 @@ -22256,7 +25086,7 @@ void M5(out S2 p) { static S2 Inner2(scoped ref S s) => new S2 { S = s }; } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields, options: TestOptions.UnsafeDebugDll); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.UnsafeDebugDll); comp.VerifyDiagnostics( // (13,9): warning CS9085: This ref-assigns 'this.field' to 'refField' but 'this.field' has a narrower escape scope than 'refField'. // p.refField = ref this.field; // 1 @@ -22300,7 +25130,7 @@ public RefByteContainer Create(ref ByteContainer bc) } """; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -22328,7 +25158,7 @@ static RS M2() } } """; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (13,16): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return new RS(ref i); @@ -22357,7 +25187,7 @@ public RS(ref int i) static void M2(ref int i, out RS rs) => rs = new RS(ref i); } """; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,50): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // static void M1(ref int i, ref RS rs) => rs = new RS(ref i); // 1 @@ -22386,7 +25216,7 @@ public RS(in int i) static void M2(in int i, out RS rs) => rs = new RS(in i); } """; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (9,49): error CS8347: Cannot use a result of 'RS.RS(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // static void M1(in int i, ref RS rs) => rs = new RS(in i); // 1 @@ -22418,7 +25248,7 @@ public RS(ref int i) static void M3(ref RS i, [UnscopedRef] out RS rs) => rs = new RS(ref i.field); } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (11,49): error CS8347: Cannot use a result of 'RS.RS(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // static void M1(ref RS i, ref RS rs) => rs = new RS(ref i.field); // 1 @@ -22453,7 +25283,7 @@ static void F1(ref R x) } } """; - var comp = CreateCompilation(source, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } @@ -22485,7 +25315,7 @@ public void Test() } } """; - var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, runtimeFeature: RuntimeFlag.ByRefFields); + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (18,9): error CS8350: This combination of arguments to 'Program.F2(ref R, out R)' is disallowed because it may expose variables referenced by parameter 'a' outside of their declaration scope // F2(ref local, out local); // 1 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index a60fc4d8dc062..cc76a1e83d1d0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -308,10 +308,10 @@ class C ref int M5(ref int rrw) => ref (rrw = ref _ro); }"); comp.VerifyDiagnostics( - // (13,36): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + // (13,36): error CS8333: Cannot return variable 'rro' by writable reference because it is a readonly variable // ref int M4(in int rro) => ref (rro = ref _rw); - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "rro = ref _rw").WithArguments("variable", "in int").WithLocation(13, 36), - // (15,47): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "rro = ref _rw").WithArguments("variable", "rro").WithLocation(13, 36), + // (15,47): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) // ref int M5(ref int rrw) => ref (rrw = ref _ro); Diagnostic(ErrorCode.ERR_AssgReadonly, "_ro").WithLocation(15, 47)); } @@ -330,12 +330,12 @@ static void M(){ } }"; CreateCompilation(source).VerifyDiagnostics( - // (5,37): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + // (5,37): error CS8333: Cannot return variable 'i' by writable reference because it is a readonly variable // ref int M1(in int i) => ref i; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "i").WithArguments("variable", "in int").WithLocation(5, 37), - // (6,43): error CS8333: Cannot return variable 'in int' by writable reference because it is a readonly variable + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "i").WithArguments("variable", "i").WithLocation(5, 37), + // (6,43): error CS8333: Cannot return variable 'i' by writable reference because it is a readonly variable // ref int M2(in int i) { return ref i; } - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "i").WithArguments("variable", "in int").WithLocation(6, 43) + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "i").WithArguments("variable", "i").WithLocation(6, 43) ); } @@ -424,9 +424,9 @@ void M(in int x) } }"); comp.VerifyDiagnostics( - // (6,30): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (6,30): error CS8329: Cannot use variable 'x' as a ref or out value because it is a readonly variable // for (ref int i = ref x; i < 0; i++) {} - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "x").WithArguments("variable", "in int").WithLocation(6, 30)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "x").WithArguments("variable", "x").WithLocation(6, 30)); } [Fact] @@ -501,9 +501,9 @@ public StructEnum(int[] arr) // (10,40): error CS1510: A ref or out value must be an assignable variable // foreach (ref readonly var v in new int[0]) Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new int[0]").WithLocation(10, 40), - // (13,31): error CS8331: Cannot assign to method 'RefEnumerable.StructEnum.Current.get' or use it as the right hand side of a ref assignment because it is a readonly variable + // (13,31): error CS8331: Cannot assign to method 'Current.get' or use it as the right hand side of a ref assignment because it is a readonly variable // foreach (ref var v in new RefEnumerable()) - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "new RefEnumerable()").WithArguments("method", "RefEnumerable.StructEnum.Current.get").WithLocation(13, 31)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "new RefEnumerable()").WithArguments("method", "Current.get").WithLocation(13, 31)); } [Fact] @@ -1444,9 +1444,9 @@ void L(ref int x, in int y) } }"); comp.VerifyDiagnostics( - // (9,19): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable + // (9,19): error CS8329: Cannot use variable 'y' as a ref or out value because it is a readonly variable // L(ref y, x); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(9, 19), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "y").WithLocation(9, 19), // (10,26): error CS1615: Argument 2 may not be passed with the 'ref' keyword // L(ref x, ref x); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "x").WithArguments("2", "ref").WithLocation(10, 26), @@ -1493,9 +1493,9 @@ void L(ref int p) { } // (16,11): error CS1620: Argument 1 must be passed with the 'ref' keyword // L(L2()); Diagnostic(ErrorCode.ERR_BadArgRef, "L2()").WithArguments("1", "ref").WithLocation(16, 11), - // (17,15): error CS8406: Cannot use method 'L2()' as a ref or out value because it is a readonly variable + // (17,15): error CS8329: Cannot use method 'L2' as a ref or out value because it is a readonly variable // L(ref L2()); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L2()").WithArguments("method", "L2()").WithLocation(17, 15)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L2()").WithArguments("method", "L2").WithLocation(17, 15)); } [Fact] @@ -1515,9 +1515,9 @@ void M() } }"); comp.VerifyDiagnostics( - // (8,25): error CS8406: Cannot use method 'L()' as a ref or out value because it is a readonly variable + // (8,25): error CS8329: Cannot use method 'L' as a ref or out value because it is a readonly variable // ref int w = ref L(); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L()").WithArguments("method", "L()").WithLocation(8, 25), + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L()").WithArguments("method", "L").WithLocation(8, 25), // (10,17): error CS8172: Cannot initialize a by-reference variable with a value // ref int y = x; Diagnostic(ErrorCode.ERR_InitializeByReferenceVariableWithValue, "y = x").WithLocation(10, 17), @@ -3178,7 +3178,7 @@ static void M() "; CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (8,26): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (8,26): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // ref int rl = ref P; Diagnostic(ErrorCode.ERR_RefProperty, "P").WithLocation(8, 26)); } @@ -3199,7 +3199,7 @@ void M() "; CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (8,26): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (8,26): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // ref int rl = ref this[0]; Diagnostic(ErrorCode.ERR_RefProperty, "this[0]").WithLocation(8, 26)); } @@ -4754,5 +4754,159 @@ public void Dispose() {} Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 7) ); } + + [Fact] + [WorkItem(64259, "https://github.com/dotnet/roslyn/issues/64259")] + public void RefLocalInDeconstruct_01() + { + var code = @" +class C +{ + static void Main() + { + int x = 0, y = 0; + (ref int a, ref readonly int b) = (x, y); + } +} +"; + + CreateCompilation(code).VerifyEmitDiagnostics( + // (7,10): error CS9072: A deconstruction variable cannot be declared as a ref local + // (ref int a, ref readonly int b) = (x, y); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(7, 10), + // (7,21): error CS9072: A deconstruction variable cannot be declared as a ref local + // (ref int a, ref readonly int b) = (x, y); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(7, 21) + ); + } + + [Fact] + [WorkItem(64259, "https://github.com/dotnet/roslyn/issues/64259")] + public void RefLocalInDeconstruct_02() + { + var code = @" +class C +{ + static void Main() + { + int x = 0, y = 0, z = 0; + (ref var a, ref var (b, c)) = (x, (y, z)); + (ref int d, var e) = (x, y); + } +} +"; + + var comp = CreateCompilation(code).VerifyEmitDiagnostics( + // (7,10): error CS9072: A deconstruction variable cannot be declared as a ref local + // (ref var a, ref var (b, c)) = (x, (y, z)); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(7, 10), + // (7,21): error CS1525: Invalid expression term 'ref' + // (ref var a, ref var (b, c)) = (x, (y, z)); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref var (b, c)").WithArguments("ref").WithLocation(7, 21), + // (7,21): error CS1073: Unexpected token 'ref' + // (ref var a, ref var (b, c)) = (x, (y, z)); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(7, 21), + // (8,10): error CS9072: A deconstruction variable cannot be declared as a ref local + // (ref int d, var e) = (x, y); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(8, 10) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + foreach (var decl in decls) + { + var type = decl.Type; + + if (type is RefTypeSyntax refType) + { + Assert.Null(model.GetSymbolInfo(type).Symbol); + Assert.Null(model.GetTypeInfo(type).Type); + + type = refType.Type; + } + + Assert.Equal("System.Int32", model.GetSymbolInfo(type).Symbol.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(type).Type.ToTestDisplayString()); + } + } + + [Fact] + [WorkItem(64259, "https://github.com/dotnet/roslyn/issues/64259")] + public void RefLocalInDeconstruct_03() + { + var code = @" +int x = 0, y = 0, z = 0; +(ref var d, var e) = (x, y); +(ref int f, ref var _) = (x, y); +"; + + var comp = CreateCompilation(code, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script); + comp.VerifyEmitDiagnostics( + // (3,2): error CS1073: Unexpected token 'ref' + // (ref var d, var e) = (x, y); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(3, 2), + // (4,2): error CS1073: Unexpected token 'ref' + // (ref int f, ref var _) = (x, y); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 2), + // (4,13): error CS9072: A deconstruction variable cannot be declared as a ref local + // (ref int f, ref var _) = (x, y); + Diagnostic(ErrorCode.ERR_DeconstructVariableCannotBeByRef, "ref").WithLocation(4, 13) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal(3, decls.Length); + + foreach (var decl in decls) + { + var f = model.GetDeclaredSymbol(decl).GetSymbol(); + + Assert.Equal(RefKind.None, f.RefKind); + Assert.Equal("System.Int32", f.Type.ToTestDisplayString()); + } + } + + [Fact] + public void RefLocalInOutVar_01() + { + var code = @" +M(out ref var a); +M(out ref int b); +M(out ref var _); + +void M(out int x) => throw null; +"; + + var comp = CreateCompilation(code, options: TestOptions.ReleaseExe.WithScriptClassName("Script"), parseOptions: TestOptions.Script); + comp.VerifyEmitDiagnostics( + // (2,7): error CS1073: Unexpected token 'ref' + // M(out ref var a); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(2, 7), + // (3,7): error CS1073: Unexpected token 'ref' + // M(out ref int b); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(3, 7), + // (4,7): error CS8388: An out variable cannot be declared as a ref local + // M(out ref var _); + Diagnostic(ErrorCode.ERR_OutVariableCannotBeByRef, "ref var").WithLocation(4, 7) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + Assert.Equal(2, decls.Length); + + foreach (var decl in decls) + { + var f = model.GetDeclaredSymbol(decl).GetSymbol(); + + Assert.Equal(RefKind.None, f.RefKind); + Assert.Equal("System.Int32", f.Type.ToTestDisplayString()); + } + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index 957d2f7d11fc5..20e2ff666817e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -8042,10 +8042,10 @@ void M() } "; CreateCompilation(text).VerifyDiagnostics( - // (14,15): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (14,15): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // M(ref P); // CS0206 Diagnostic(ErrorCode.ERR_RefProperty, "P").WithLocation(14, 15), - // (15,15): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (15,15): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // M(out this.Q); // CS0206 Diagnostic(ErrorCode.ERR_RefProperty, "this.Q").WithLocation(15, 15)); } @@ -8072,10 +8072,10 @@ void M() } "; CreateCompilation(text).VerifyDiagnostics( - // (13,15): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (13,15): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // R(ref this[0]); // CS0206 Diagnostic(ErrorCode.ERR_RefProperty, "this[0]").WithLocation(13, 15), - // (14,15): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (14,15): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // O(out this[0]); // CS0206 Diagnostic(ErrorCode.ERR_RefProperty, "this[0]").WithLocation(14, 15)); } @@ -22856,13 +22856,13 @@ static void Test(Func Baz) // (17,47): error CS1510: A ref or out argument must be an assignable variable // var z5 = new Func(ref Goo(x => x)); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "Goo(x => x)").WithLocation(17, 47), - // (18,43): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (18,43): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var z6 = new Func(ref BarP); Diagnostic(ErrorCode.ERR_RefProperty, "ref BarP").WithLocation(18, 43), // (19,47): error CS1510: A ref or out argument must be an assignable variable // var z7 = new Func(ref new Func(x => x)); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new Func(x => x)").WithLocation(19, 47), - // (20,43): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (20,43): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var z8 = new Func(ref Program.BarP); Diagnostic(ErrorCode.ERR_RefProperty, "ref Program.BarP").WithLocation(20, 43), // (21,47): error CS1510: A ref or out argument must be an assignable variable @@ -22889,13 +22889,13 @@ static void Test(Func Baz) // (17,47): error CS1510: A ref or out argument must be an assignable variable // var z5 = new Func(ref Goo(x => x)); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "Goo(x => x)").WithLocation(17, 47), - // (18,47): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (18,47): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var z6 = new Func(ref BarP); Diagnostic(ErrorCode.ERR_RefProperty, "BarP").WithLocation(18, 47), // (19,47): error CS1510: A ref or out argument must be an assignable variable // var z7 = new Func(ref new Func(x => x)); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new Func(x => x)").WithLocation(19, 47), - // (20,47): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (20,47): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var z8 = new Func(ref Program.BarP); Diagnostic(ErrorCode.ERR_RefProperty, "Program.BarP").WithLocation(20, 47), // (21,47): error CS1510: A ref or out argument must be an assignable variable @@ -23007,7 +23007,7 @@ static void Test(Func Baz) // (10,46): error CS0149: Method name expected // var c = new Func(ref Baz, ref Baz.Invoke); Diagnostic(ErrorCode.ERR_MethodNameExpected, "Baz, ref Baz.Invoke").WithLocation(10, 46), - // (11,42): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (11,42): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var d = new Func(ref BarP, BarP.Invoke); Diagnostic(ErrorCode.ERR_RefProperty, "ref BarP").WithLocation(11, 42), // (11,46): error CS0149: Method name expected @@ -23016,7 +23016,7 @@ static void Test(Func Baz) // (12,42): error CS0149: Method name expected // var e = new Func(BarP, ref BarP.Invoke); Diagnostic(ErrorCode.ERR_MethodNameExpected, "BarP, ref BarP.Invoke").WithLocation(12, 42), - // (13,42): error CS0206: A property or indexer may not be passed as an out or ref parameter + // (13,42): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value // var f = new Func(ref BarP, ref BarP.Invoke); Diagnostic(ErrorCode.ERR_RefProperty, "ref BarP").WithLocation(13, 42), // (13,46): error CS0149: Method name expected diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index 3d89f9b76aae1..c6ab1b3fb8bb8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -1671,39 +1671,39 @@ static void M2(scoped ref Span x, out Span y) // In C# 7.2 the input to an `out` parameter can escape which means several // of the tests are errors due to stack copying to escape comp.VerifyDiagnostics( - // (16,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // (16,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope // M2(ref s1, out s2); // one - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(16, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "y").WithLocation(16, 9), // (16,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s1, out s2); // one Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(16, 24), - // (17,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (17,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(ref s2, out s1); // two - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(17, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(17, 9), // (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s2, out s1); // two Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16), - // (19,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // (19,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope // M2(ref s1, out s2); // three - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(19, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s1, out s2)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "y").WithLocation(19, 9), // (19,24): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s1, out s2); // three Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(19, 24), - // (20,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (20,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(ref s2, out s1); // four - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(20, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(20, 9), // (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s2, out s1); // four Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16), - // (22,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope + // (22,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'y' outside of their declaration scope // M2(y: out s2, x: ref s1); // five - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s2, x: ref s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "y").WithLocation(22, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s2, x: ref s1)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "y").WithLocation(22, 9), // (22,19): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(y: out s2, x: ref s1); // five Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(22, 19), - // (23,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (23,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(y: out s1, x: ref s2); // six - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(23, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(23, 9), // (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(y: out s1, x: ref s2); // six Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30), @@ -1714,21 +1714,21 @@ static void M2(scoped ref Span x, out Span y) else { comp.VerifyDiagnostics( - // (17,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (17,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(ref s2, out s1); // two - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(17, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(17, 9), // (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s2, out s1); // two Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16), - // (20,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (20,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(ref s2, out s1); // four - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(20, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(20, 9), // (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(ref s2, out s1); // four Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16), - // (23,9): error CS8350: This combination of arguments to 'C.M2(ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope + // (23,9): error CS8350: This combination of arguments to 'C.M2(scoped ref Span, out Span)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope // M2(y: out s1, x: ref s2); // six - Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(ref System.Span, out System.Span)", "x").WithLocation(23, 9), + Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2(scoped ref System.Span, out System.Span)", "x").WithLocation(23, 9), // (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // M2(y: out s1, x: ref s2); // six Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30)); 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/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs index 476cd004055e3..1c2571781afe4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs @@ -64,6 +64,100 @@ static void Main() ); } + [Fact] + public void ImplicitConversions_02() + { + var source = @" +using System; +class C +{ + static void Main() + { + } + + static byte[] Test1() => ""hello""U8; + static Span Test2() => ""dog""U8; +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (9,30): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'byte[]' + // static byte[] Test1() => "hello"U8; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hello""U8").WithArguments("System.ReadOnlySpan", "byte[]").WithLocation(9, 30), + // (10,34): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.Span' + // static Span Test2() => "dog"U8; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""dog""U8").WithArguments("System.ReadOnlySpan", "System.Span").WithLocation(10, 34) + ); + } + + [Fact] + public void ImplicitConversions_03() + { + var source = @" +using System; +class C +{ + static void Main() + { + } + + const string nullConstant = null; + static byte[] Test1() => nullConstant; + static Span Test2() => nullConstant; + static ReadOnlySpan Test3() => nullConstant; +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (10,30): error CS0029: Cannot implicitly convert type 'string' to 'byte[]' + // static byte[] Test1() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "nullConstant").WithArguments("string", "byte[]").WithLocation(10, 30), + // (11,34): error CS0029: Cannot implicitly convert type 'string' to 'System.Span' + // static Span Test2() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "nullConstant").WithArguments("string", "System.Span").WithLocation(11, 34), + // (12,42): error CS0029: Cannot implicitly convert type 'string' to 'System.ReadOnlySpan' + // static ReadOnlySpan Test3() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "nullConstant").WithArguments("string", "System.ReadOnlySpan").WithLocation(12, 42) + ); + } + + [Fact] + public void ImplicitConversions_04() + { + var source = @" +using System; +class C +{ + static void Main() + { + } + + const object nullConstant = null; + static byte[] Test1() => nullConstant; + static Span Test2() => nullConstant; + static ReadOnlySpan Test3() => nullConstant; +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (10,30): error CS0266: Cannot implicitly convert type 'object' to 'byte[]'. An explicit conversion exists (are you missing a cast?) + // static byte[] Test1() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nullConstant").WithArguments("object", "byte[]").WithLocation(10, 30), + // (11,34): error CS0266: Cannot implicitly convert type 'object' to 'System.Span'. An explicit conversion exists (are you missing a cast?) + // static Span Test2() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nullConstant").WithArguments("object", "System.Span").WithLocation(11, 34), + // (11,34): error CS0266: Cannot implicitly convert type 'object' to 'System.Span'. An explicit conversion exists (are you missing a cast?) + // static Span Test2() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nullConstant").WithArguments("object", "System.Span").WithLocation(11, 34), + // (12,42): error CS0266: Cannot implicitly convert type 'object' to 'System.ReadOnlySpan'. An explicit conversion exists (are you missing a cast?) + // static ReadOnlySpan Test3() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nullConstant").WithArguments("object", "System.ReadOnlySpan").WithLocation(12, 42), + // (12,42): error CS0266: Cannot implicitly convert type 'object' to 'System.ReadOnlySpan'. An explicit conversion exists (are you missing a cast?) + // static ReadOnlySpan Test3() => nullConstant; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "nullConstant").WithArguments("object", "System.ReadOnlySpan").WithLocation(12, 42) + ); + } + [Fact] public void ImplicitConversions_TupleLiteral_01() { @@ -143,6 +237,91 @@ static void Main() ); } + [Fact] + public void ExplicitConversions_02() + { + var source = @" +using System; +class C +{ + static void Main() + { + var array = (byte[])""hello""u8; + var span = (Span)""dog""u8; + } +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + + comp.VerifyDiagnostics( + // (7,21): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'byte[]' + // var array = (byte[])"hello"u8; + Diagnostic(ErrorCode.ERR_NoExplicitConv, @"(byte[])""hello""u8").WithArguments("System.ReadOnlySpan", "byte[]").WithLocation(7, 21), + // (8,20): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.Span' + // var span = (Span)"dog"u8; + Diagnostic(ErrorCode.ERR_NoExplicitConv, @"(Span)""dog""u8").WithArguments("System.ReadOnlySpan", "System.Span").WithLocation(8, 20) + ); + } + + [Fact] + public void ExplicitConversions_03() + { + var source = @" +using System; +class C +{ + static void Main() + { + const string nullConstant = null; + var array = (byte[])nullConstant; + var span = (Span)nullConstant; + var readonlySpan = (ReadOnlySpan)nullConstant; + } +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + + comp.VerifyDiagnostics( + // (8,21): error CS0030: Cannot convert type 'string' to 'byte[]' + // var array = (byte[])nullConstant; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(byte[])nullConstant").WithArguments("string", "byte[]").WithLocation(8, 21), + // (9,20): error CS0030: Cannot convert type 'string' to 'System.Span' + // var span = (Span)nullConstant; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)nullConstant").WithArguments("string", "System.Span").WithLocation(9, 20), + // (10,28): error CS0030: Cannot convert type 'string' to 'System.ReadOnlySpan' + // var readonlySpan = (ReadOnlySpan)nullConstant; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)nullConstant").WithArguments("string", "System.ReadOnlySpan").WithLocation(10, 28) + ); + } + + [Fact] + public void ExplicitConversions_04() + { + var source = @"#pragma warning disable CS0219 // The variable is assigned but its value is never used +using System; +class C +{ + static void Main() + { + const object nullConstant = null; + var array = (byte[])nullConstant; + var span = (Span)nullConstant; + var readonlySpan = (ReadOnlySpan)nullConstant; + } +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); + + comp.VerifyDiagnostics( + // (9,20): error CS0457: Ambiguous user defined conversions 'Span.implicit operator Span(ArraySegment)' and 'Span.implicit operator Span(byte[]?)' when converting from 'object' to 'Span' + // var span = (Span)nullConstant; + Diagnostic(ErrorCode.ERR_AmbigUDConv, "(Span)nullConstant").WithArguments("System.Span.implicit operator System.Span(System.ArraySegment)", "System.Span.implicit operator System.Span(byte[]?)", "object", "System.Span").WithLocation(9, 20), + // (10,28): error CS0457: Ambiguous user defined conversions 'ReadOnlySpan.implicit operator ReadOnlySpan(ArraySegment)' and 'ReadOnlySpan.implicit operator ReadOnlySpan(byte[]?)' when converting from 'object' to 'ReadOnlySpan' + // var readonlySpan = (ReadOnlySpan)nullConstant; + Diagnostic(ErrorCode.ERR_AmbigUDConv, "(ReadOnlySpan)nullConstant").WithArguments("System.ReadOnlySpan.implicit operator System.ReadOnlySpan(System.ArraySegment)", "System.ReadOnlySpan.implicit operator System.ReadOnlySpan(byte[]?)", "object", "System.ReadOnlySpan").WithLocation(10, 28) + ); + } + [Fact] public void ExplicitConversions_TupleLiteral_01() { @@ -2342,6 +2521,36 @@ static void Main() ); } + [Fact] + public void NotConstant_01() + { + var source = @"#pragma warning disable CS0219 // The variable 'y' is assigned but its value is never used +using System; +class C +{ + const ReadOnlySpan x = ""07""U8; + + static void Main() + { + const ReadOnlySpan y = ""08""U8; + } +} +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugDll); + + comp.VerifyEmitDiagnostics( + // (5,11): error CS8345: Field or auto-implemented property cannot be of type 'ReadOnlySpan' unless it is an instance member of a ref struct. + // const ReadOnlySpan x = "07"U8; + Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 11), + // (5,34): error CS0133: The expression being assigned to 'C.x' must be constant + // const ReadOnlySpan x = "07"U8; + Diagnostic(ErrorCode.ERR_NotConstantExpression, @"""07""U8").WithArguments("C.x").WithLocation(5, 34), + // (9,15): error CS0283: The type 'ReadOnlySpan' cannot be declared const + // const ReadOnlySpan y = "08"U8; + Diagnostic(ErrorCode.ERR_BadConstType, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(9, 15) + ); + } + [Fact] public void DefaultParameterValues_01() { diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 8455823a932c8..16c046c7e2e19 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -1466,6 +1466,7 @@ enum C bool success = model.TryGetSpeculativeSemanticModel(equalsValue.SpanStart, newEqualsValue, out speculativeModel); Assert.True(success); Assert.NotNull(speculativeModel); + Assert.False(speculativeModel.IgnoresAccessibility); var typeInfo = speculativeModel.GetTypeInfo(expr); Assert.NotNull(typeInfo.Type); @@ -1475,6 +1476,10 @@ enum C var constantInfo = speculativeModel.GetConstantValue(expr); Assert.True(constantInfo.HasValue, "must be a constant"); Assert.Equal((short)0, constantInfo.Value); + + model = compilation.GetSemanticModel(tree, ignoreAccessibility: true); + model.TryGetSpeculativeSemanticModel(equalsValue.SpanStart, newEqualsValue, out speculativeModel); + Assert.True(speculativeModel.IgnoresAccessibility); } [Fact] @@ -2699,6 +2704,7 @@ private static void TestGetSpeculativeSemanticModelForTypeSyntax_Common( var success = model.TryGetSpeculativeSemanticModel(position, speculatedTypeSyntax, out speculativeModel, bindingOption); Assert.True(success); Assert.NotNull(speculativeModel); + Assert.False(speculativeModel.IgnoresAccessibility); Assert.True(speculativeModel.IsSpeculativeSemanticModel); Assert.Equal(model, speculativeModel.ParentModel); @@ -2830,6 +2836,11 @@ class MyException : System.Exception var speculatedTypeExpression = SyntaxFactory.ParseName("System.ArgumentException"); TestGetSpeculativeSemanticModelForTypeSyntax_Common(model, baseList.SpanStart, speculatedTypeExpression, SpeculativeBindingOption.BindAsTypeOrNamespace, SymbolKind.NamedType, "System.ArgumentException"); + + model = compilation.GetSemanticModel(tree, ignoreAccessibility: true); + SemanticModel speculativeModel; + model.TryGetSpeculativeSemanticModel(baseList.SpanStart, speculatedTypeExpression, out speculativeModel); + Assert.True(speculativeModel.IgnoresAccessibility); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj b/src/Compilers/CSharp/Test/Symbol/Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj index 65c51a84985e6..8c60405ebc693 100644 --- a/src/Compilers/CSharp/Test/Symbol/Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Symbol/Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests true - net6.0;net472 + net7.0;net472 true diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index cc301ceaac666..1564fddbafef2 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -8030,7 +8030,7 @@ void M() Verify(type.ToDisplayParts(), "", SymbolDisplayPartKind.DelegateName); - Verify(type.ToDisplayParts(s_fullDelegateFormat), "delegate string (ref int)", + Verify(type.ToDisplayParts(s_fullDelegateFormat), "delegate string (ref int arg)", SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.Keyword, @@ -8040,6 +8040,8 @@ void M() SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); } @@ -8196,7 +8198,7 @@ public void ScopedParameter_01() @"ref struct R { } class Program { - static void F(scoped R r1, scoped ref R r2, scoped in R r3) { } + static void F(scoped R r1, scoped ref R r2, scoped in R r3, scoped out R r4) { } }"; var comp = CreateCompilation(source); @@ -8204,12 +8206,10 @@ static void F(scoped R r1, scoped ref R r2, scoped in R r3) { } var method = comp.GetMember("Program.F"); var formatTypeOnly = SymbolDisplayFormat.TestFormat.WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName); - var formatTypeAndRef = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut); - var formatTypeAndScoped = formatTypeOnly.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); - var formatTypeRefAndScoped = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut).WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); ; + var formatTypeRefAndScoped = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut); - Verify(method.ToDisplayParts(formatTypeAndRef), - "void Program.F(R r1, ref R r2, in R r3)", + Verify(method.ToDisplayParts(formatTypeOnly), + "void Program.F(R r1, R r2, R r3, R r4)", SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ClassName, @@ -8221,25 +8221,23 @@ static void F(scoped R r1, scoped ref R r2, scoped in R r3) { } SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation, SymbolDisplayPartKind.Space, - SymbolDisplayPartKind.Keyword, - SymbolDisplayPartKind.Space, SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation, SymbolDisplayPartKind.Space, - SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ParameterName, + SymbolDisplayPartKind.Punctuation, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); - Verify(method.ToDisplayParts(formatTypeAndScoped), - "void Program.F(scoped R r1, R r2, R r3)"); - Verify(method.ToDisplayParts(formatTypeRefAndScoped), - "void Program.F(scoped R r1, scoped ref R r2, scoped in R r3)", + "void Program.F(scoped R r1, scoped ref R r2, scoped in R r3, out R r4)", SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ClassName, @@ -8269,6 +8267,13 @@ static void F(scoped R r1, scoped ref R r2, scoped in R r3) { } SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); } @@ -8277,7 +8282,7 @@ public void ScopedParameter_02() { var source = @"ref struct R { } -delegate void D(scoped R r1, scoped ref R r2, scoped in R r3); +delegate void D(scoped R r1, scoped ref R r2, scoped in R r3, scoped out R r4); "; var comp = CreateCompilation(source); @@ -8285,18 +8290,13 @@ public void ScopedParameter_02() var delegateType = comp.GetMember("D"); var formatTypeOnly = s_fullDelegateFormat.WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName); - var formatTypeAndRef = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut); - var formatTypeAndScoped = formatTypeOnly.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); - var formatTypeRefAndScoped = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut).WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); - - Verify(delegateType.ToDisplayParts(formatTypeAndRef), - "delegate void D(R r1, ref R r2, in R r3)"); + var formatTypeRefAndScoped = formatTypeOnly.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut); - Verify(delegateType.ToDisplayParts(formatTypeAndScoped), - "delegate void D(scoped R r1, R r2, R r3)"); + Verify(delegateType.ToDisplayParts(formatTypeOnly), + "delegate void D(R r1, R r2, R r3, R r4)"); Verify(delegateType.ToDisplayParts(formatTypeRefAndScoped), - "delegate void D(scoped R r1, scoped ref R r2, scoped in R r3)"); + "delegate void D(scoped R r1, scoped ref R r2, scoped in R r3, out R r4)"); } [Fact] @@ -8307,33 +8307,36 @@ public void ScopedParameter_03() ref struct R { } unsafe class Program { - delegate* D; + delegate* D; } "; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics( // (5,15): error CS8755: 'scoped' cannot be used as a modifier on a function pointer parameter. - // delegate* D; + // delegate* D; Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "scoped").WithArguments("scoped").WithLocation(5, 15), // (5,25): error CS8755: 'scoped' cannot be used as a modifier on a function pointer parameter. - // delegate* D; + // delegate* D; Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "scoped").WithArguments("scoped").WithLocation(5, 25), // (5,38): error CS8755: 'scoped' cannot be used as a modifier on a function pointer parameter. - // delegate* D; - Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "scoped").WithArguments("scoped").WithLocation(5, 38)); + // delegate* D; + Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "scoped").WithArguments("scoped").WithLocation(5, 38), + // (5,52): error CS8755: 'scoped' cannot be used as a modifier on a function pointer parameter. + // delegate* D; + Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "scoped").WithArguments("scoped").WithLocation(5, 52)); var type = comp.GetMember("Program.D").Type; var formatMinimal = new SymbolDisplayFormat(); var formatTypeRefAndScoped = s_fullDelegateFormat. - WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut).WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); + WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut); Verify(type.ToDisplayParts(formatMinimal), - "delegate*"); + "delegate*"); Verify(type.ToDisplayParts(formatTypeRefAndScoped), - "delegate*"); + "delegate*"); } [Fact] @@ -8352,8 +8355,7 @@ class Program comp.VerifyDiagnostics(); var format = SymbolDisplayFormat.TestFormat. - WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut). - WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); + WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut); Verify(comp.GetMember("Program.F1").ToDisplayParts(format), "void Program.F1(out System.Int32 i1, out System.Int32 i2)"); @@ -8384,11 +8386,9 @@ static void M(R r0) var locals = decls.Select(d => model.GetDeclaredSymbol(d)).ToArray(); var formatTypeOnly = SymbolDisplayFormat.TestFormat.WithLocalOptions(SymbolDisplayLocalOptions.IncludeType); - var formatTypeAndRef = formatTypeOnly.AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef); - var formatTypeAndScoped = formatTypeOnly.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); - var formatTypeRefAndScoped = formatTypeOnly.AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef).WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); + var formatTypeRefAndScoped = formatTypeOnly.AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef); - Verify(locals[0].ToDisplayParts(formatTypeAndRef), + Verify(locals[0].ToDisplayParts(formatTypeOnly), "R r1", SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, @@ -8402,12 +8402,8 @@ static void M(R r0) SymbolDisplayPartKind.Space, SymbolDisplayPartKind.LocalName); - Verify(locals[1].ToDisplayParts(formatTypeAndRef), - "ref readonly R r3", - SymbolDisplayPartKind.Keyword, - SymbolDisplayPartKind.Space, - SymbolDisplayPartKind.Keyword, - SymbolDisplayPartKind.Space, + Verify(locals[1].ToDisplayParts(formatTypeOnly), + "R r3", SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.LocalName); @@ -8423,12 +8419,6 @@ static void M(R r0) SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.LocalName); - - Verify(locals[1].ToDisplayParts(formatTypeAndScoped), - "R r3", - SymbolDisplayPartKind.StructName, - SymbolDisplayPartKind.Space, - SymbolDisplayPartKind.LocalName); } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CovariantReturnTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CovariantReturnTests.cs index 19dab3bbcd7fb..cc8706fbd9781 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CovariantReturnTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CovariantReturnTests.cs @@ -503,7 +503,7 @@ private static CSharpCompilation RetargetingView( MetadataReference alternateCorlib = (coreLibrary == CorelibraryWithCovariantReturnSupport1) ? CorelibraryWithCovariantReturnSupport2 : (coreLibrary == CorelibraryWithoutCovariantReturnSupport1) ? CorelibraryWithoutCovariantReturnSupport2 : - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); references = references.Prepend(alternateCorlib).ToArray(); } var parseOptions = (CSharpParseOptions)comp.SyntaxTrees[0].Options; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 9b4d75e2af10d..0199f24e950fe 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -51,20 +51,20 @@ private static Verification Verify(bool isStatic) return isStatic ? Verification.Skipped : VerifyOnMonoOrCoreClr; } - private static bool Execute(bool isStatic, bool haveImplementationInDerivedInterface = false) + private static bool Execute(bool isStatic, bool haveImplementationInDerivedInterface = false, bool hasImplementationOfVirtualInDerivedType = false) { - // https://github.com/dotnet/roslyn/issues/61321 : Enable execution for isStatic and haveImplementationInDerivedInterface once runtime can handle it. - if (!ExecutionConditionUtil.IsMonoOrCoreClr || (isStatic && haveImplementationInDerivedInterface)) + // The runtime ignores the implementation of a static virtual method in derived types + // Tracked by https://github.com/dotnet/roslyn/issues/64501 + if (isStatic && hasImplementationOfVirtualInDerivedType) { return false; } -#if !NET7_0_OR_GREATER - if (isStatic) + // https://github.com/dotnet/roslyn/issues/61321 : Enable execution for isStatic and haveImplementationInDerivedInterface once runtime can handle it. + if (!ExecutionConditionUtil.IsMonoOrCoreClr || (isStatic && haveImplementationInDerivedInterface)) { return false; } -#endif return true; } @@ -804,7 +804,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"Test.M1 Test.M2", verify: Verify(isStatic), @@ -906,7 +906,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"Test.M1 Test.M2", verify: Verify(isStatic), @@ -1139,7 +1139,7 @@ class Test2 : I1 Assert.Equal("void Test2.I1.M1()", test1.FindImplementationForInterfaceMember(m1).ToTestDisplayString()); CompileAndVerify(compilation1, - expectedOutput: Execute(isStatic) ? "Test2.M1" : null, + expectedOutput: Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? "Test2.M1" : null, verify: Verify(isStatic), symbolValidator: (m) => { @@ -1218,7 +1218,7 @@ class Test2 : I1 Assert.Equal("void Test2.M1()", test1.FindImplementationForInterfaceMember(m1).ToTestDisplayString()); CompileAndVerify(compilation1, - expectedOutput: Execute(isStatic) ? "Test2.M1" : null, + expectedOutput: Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? "Test2.M1" : null, verify: Verify(isStatic), symbolValidator: (m) => { @@ -1472,7 +1472,7 @@ class Test2 : I1 Assert.Equal("System.Int32 Test2.I1.M2()", test1.FindImplementationForInterfaceMember(m2).ToTestDisplayString()); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"Test2.M1 2", verify: Verify(isStatic), @@ -1560,7 +1560,7 @@ class Test2 : I1 Assert.Equal("System.Int32 Test2.M2()", test1.FindImplementationForInterfaceMember(m2).ToTestDisplayString()); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"Test2.M1 2", verify: Verify(isStatic), @@ -3836,7 +3836,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"100 200 300 @@ -3988,7 +3988,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"100 200 300 @@ -6870,7 +6870,7 @@ class Test : I1 {} ValidateEventImplementation_201(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"add E7 remove E7 add E8 @@ -6977,7 +6977,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"add E7 remove E7 add E8 @@ -7084,7 +7084,7 @@ void Validate(ModuleSymbol m) Validate(compilation1.SourceModule); CompileAndVerify(compilation1, - expectedOutput: !Execute(isStatic) ? null : + expectedOutput: !Execute(isStatic, hasImplementationOfVirtualInDerivedType: true) ? null : @"add E7 remove E7 add E8 @@ -12034,7 +12034,7 @@ void I1.M1() "; var compilation1 = CreateCompilation(source1 + source2, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation1.VerifyDiagnostics(); @@ -12044,7 +12044,7 @@ void I1.M1() var compilation2 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation2.VerifyDiagnostics(); @@ -12095,7 +12095,7 @@ void I1.M1() "; var compilation1 = CreateCompilation(source1 + source2, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation1.VerifyDiagnostics(); @@ -12105,7 +12105,7 @@ void I1.M1() var compilation2 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation2.VerifyDiagnostics(); @@ -12156,7 +12156,7 @@ void I1.M1() "; var compilation1 = CreateCompilation(source1 + source2, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation1.VerifyDiagnostics(); @@ -12166,7 +12166,7 @@ void I1.M1() var compilation2 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation2.VerifyDiagnostics(); @@ -45657,13 +45657,13 @@ public class Test1 : I1 "; var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); compilation0.VerifyDiagnostics(); var compilation1 = CreateCompilation(source1, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular, - targetFramework: TargetFramework.NetCoreApp); + targetFramework: TargetFramework.Net50); Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); compilation1.VerifyDiagnostics(); @@ -50163,7 +50163,7 @@ public TypeIdentifierAttribute(string scope, string identifier){} [Fact] public void NoPia_01() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50182,7 +50182,7 @@ void M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia7 : ITest33 @@ -50190,7 +50190,7 @@ class UsePia7 : ITest33 } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (2,17): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50202,7 +50202,7 @@ class UsePia7 : ITest33 [Fact] public void NoPia_02() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50221,7 +50221,7 @@ void M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia @@ -50233,7 +50233,7 @@ public static void Main(ITest33 x) } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,29): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50245,7 +50245,7 @@ public static void Main(ITest33 x) [Fact] public void NoPia_03() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50265,7 +50265,7 @@ void M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia @@ -50277,7 +50277,7 @@ public static void Main(ITest33 x) } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,29): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50289,7 +50289,7 @@ public static void Main(ITest33 x) [Fact] public void NoPia_04() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50308,7 +50308,7 @@ sealed void M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia @@ -50320,7 +50320,7 @@ public static void Main(ITest33 x) } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,29): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50332,7 +50332,7 @@ public static void Main(ITest33 x) [Fact] public void NoPia_05() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50351,7 +50351,7 @@ static void M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia @@ -50363,7 +50363,7 @@ public static void Main() } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (6,9): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50375,7 +50375,7 @@ public static void Main() [Fact] public void NoPia_06() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50398,7 +50398,7 @@ public interface I1 } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); CompileAndVerify(piaCompilation, verify: VerifyOnMonoOrCoreClr); @@ -50413,7 +50413,7 @@ public static void Main(ITest33.I1 x) foreach (var reference in new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), piaCompilation.EmitToImageReference(embedInteropTypes: true) }) { - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,37): error CS1754: Type 'ITest33.I1' cannot be embedded because it is a nested type. Consider setting the 'Embed Interop Types' property to false. @@ -50426,7 +50426,7 @@ public static void Main(ITest33.I1 x) [Fact] public void NoPia_07() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50445,7 +50445,7 @@ public interface ITest33 } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); CompileAndVerify(piaCompilation, verify: VerifyOnMonoOrCoreClr); @@ -50461,7 +50461,7 @@ public static void Main() foreach (var reference in new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), piaCompilation.EmitToImageReference(embedInteropTypes: true) }) { - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (6,13): error CS8711: Type 'ITest33' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50474,7 +50474,7 @@ public static void Main() [Fact] public void NoPia_08() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50500,7 +50500,7 @@ void ITest33.M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer = @" class UsePia @@ -50512,7 +50512,7 @@ public static void Main(ITest44 x) } "; - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,29): error CS8711: Type 'ITest44' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. @@ -50524,7 +50524,7 @@ public static void Main(ITest44 x) [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NoPiaNeedsDesktop)] public void NoPia_09() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50555,7 +50555,7 @@ void ITest33.M1(){} } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); string consumer1 = @" public class UsePia @@ -50615,7 +50615,7 @@ public interface ITest33 [WorkItem(35911, "https://github.com/dotnet/roslyn/issues/35911")] public void NoPia_10() { - var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.NetCoreApp); + var attributes = CreateCompilation(NoPiaAttributes, options: TestOptions.ReleaseDll, targetFramework: TargetFramework.Net50); var attributesRef = attributes.EmitToImageReference(); string pia = @" @@ -50641,7 +50641,7 @@ public interface ITest44 : ITest33 } "; - var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, references: new[] { attributesRef }, targetFramework: TargetFramework.Net50); CompileAndVerify(piaCompilation, verify: VerifyOnMonoOrCoreClr); @@ -50657,7 +50657,7 @@ public static void Main(ITest44 x) foreach (var reference in new[] { piaCompilation.ToMetadataReference(embedInteropTypes: true), piaCompilation.EmitToImageReference(embedInteropTypes: true) }) { - var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.NetCoreApp); + var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseDll, references: new[] { reference, attributesRef }, targetFramework: TargetFramework.Net50); compilation1.VerifyEmitDiagnostics( // (4,29): error CS8750: Type 'ITest44' cannot be embedded because it has a re-abstraction of a member from base interface. Consider setting the 'Embed Interop Types' property to false. @@ -50693,7 +50693,7 @@ static void Main() } "; - var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreApp); + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net50); compilation0.VerifyDiagnostics(); var verifier = CompileAndVerify(compilation0, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @"M1", verify: VerifyOnMonoOrCoreClr); @@ -50746,7 +50746,7 @@ static void Main() } "; - var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreApp); + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net50); compilation0.VerifyDiagnostics(); var verifier = CompileAndVerify(compilation0, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @"M1", verify: VerifyOnMonoOrCoreClr); @@ -50799,7 +50799,7 @@ static void Main() } "; - var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreApp); + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net50); compilation0.VerifyDiagnostics(); var verifier = CompileAndVerify(compilation0, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @"A", verify: VerifyOnMonoOrCoreClr); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index b254d495f26f6..cfca66891dc05 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -228,7 +228,7 @@ public sealed override bool AreLocalsZeroed { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -324,7 +324,7 @@ internal override AttributeUsageInfo GetAttributeUsageInfo() return AttributeUsageInfo.Null; } - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 31fb1f7203af3..7acc138b67387 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -14839,13 +14839,6 @@ static void M02() where T : I1 private static bool Execute(bool isVirtual) { -#if !NET7_0_OR_GREATER - if (isVirtual) - { - return false; - } -#endif - return ExecutionConditionUtil.IsMonoOrCoreClr; } diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 31929e7985f6b..d22108d22bf47 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -374,6 +374,7 @@ public void WarningLevel_2() case ErrorCode.WRN_RefAssignReturnOnly: case ErrorCode.WRN_RefReturnOnlyParameter: case ErrorCode.WRN_RefReturnOnlyParameter2: + case ErrorCode.WRN_RefAssignValEscapeWider: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_InvalidVersionFormat: diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/LocationsTests.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/LocationsTests.cs index 2e2e0f46ed92f..99e72ddfe0e06 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/LocationsTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/LocationsTests.cs @@ -507,6 +507,46 @@ public void TestExternalLocationFormatting() Assert.Equal("test.txt(3,2): warning CS0000: msg", CSharpDiagnosticFormatter.Instance.Format(diagnostic, EnsureEnglishUICulture.PreferredOrNull)); } + [Fact, WorkItem(64236, "https://github.com/dotnet/roslyn/issues/64236")] + public void TestExternalLocationWithMappedPathAndSpan() + { + var filePath = "test.txt"; + var sourceSpan = new TextSpan(); + var lineSpan = new LinePositionSpan(new LinePosition(2, 1), new LinePosition(3, 1)); + var mappedFilePath = "test2.txt"; + var mappedLineSpan = new LinePositionSpan(new LinePosition(3, 2), new LinePosition(4, 2)); + + Location locationWithoutMapping = Location.Create(filePath, sourceSpan, lineSpan); + Location locationWithMapping = Location.Create(filePath, sourceSpan, lineSpan, mappedFilePath, mappedLineSpan); + Assert.NotEqual(locationWithMapping, locationWithoutMapping); + + var diagnosticWithoutMapping = CodeAnalysis.Diagnostic.Create("CS0000", "", "msg", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 1, location: locationWithoutMapping); + Assert.Equal("test.txt(3,2): warning CS0000: msg", CSharpDiagnosticFormatter.Instance.Format(diagnosticWithoutMapping, EnsureEnglishUICulture.PreferredOrNull)); + + var diagnosticWithMapping = CodeAnalysis.Diagnostic.Create("CS0000", "", "msg", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 1, location: locationWithMapping); + Assert.Equal("test2.txt(4,3): warning CS0000: msg", CSharpDiagnosticFormatter.Instance.Format(diagnosticWithMapping, EnsureEnglishUICulture.PreferredOrNull)); + + var lineInfo = locationWithoutMapping.GetLineSpan(); + Assert.Equal(filePath, lineInfo.Path); + Assert.Equal(lineSpan, lineInfo.Span); + Assert.False(lineInfo.HasMappedPath); + + var mappedLineInfo = locationWithoutMapping.GetMappedLineSpan(); + Assert.Equal(filePath, mappedLineInfo.Path); + Assert.Equal(lineSpan, mappedLineInfo.Span); + Assert.False(mappedLineInfo.HasMappedPath); + + lineInfo = locationWithMapping.GetLineSpan(); + Assert.Equal(filePath, lineInfo.Path); + Assert.Equal(lineSpan, lineInfo.Span); + Assert.False(lineInfo.HasMappedPath); + + mappedLineInfo = locationWithMapping.GetMappedLineSpan(); + Assert.Equal(mappedFilePath, mappedLineInfo.Path); + Assert.Equal(mappedLineSpan, mappedLineInfo.Span); + Assert.True(mappedLineInfo.HasMappedPath); + } + [WorkItem(1097381, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097381")] [Fact] public void TestDiagnosticsLocationsExistInsideTreeSpan() diff --git a/src/Compilers/CSharp/Test/Syntax/Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj b/src/Compilers/CSharp/Test/Syntax/Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj index 007d6b9f3c3b9..df1f00c8cf507 100644 --- a/src/Compilers/CSharp/Test/Syntax/Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Syntax/Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj @@ -7,7 +7,7 @@ false true true - net6.0;net472 + net7.0;net472 $(NoWarn);1570;1587 diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs index 477a073761a0d..c9e067e2a450c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs @@ -3317,139 +3317,146 @@ public void Local_16(LanguageVersion langVersion) EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void New_01(LanguageVersion langVersion) + [Fact] + public void Local_17() { string source = @" -new scoped(); +scoped R x; "; - - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, TestOptions.Script); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ObjectCreationExpression); + N(SyntaxKind.ScopedType); { - N(SyntaxKind.NewKeyword); + N(SyntaxKind.ScopedKeyword); N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken, "scoped"); - } - N(SyntaxKind.ArgumentList); - { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierToken, "R"); } } - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } } + N(SyntaxKind.SemicolonToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void New_02(LanguageVersion langVersion) + [Fact] + public void Local_18() { string source = @" -new scoped int(); -"; - - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type - // new scoped int(); - Diagnostic(ErrorCode.ERR_BadNewExpr, "int").WithLocation(2, 12), - // (2,12): error CS1002: ; expected - // new scoped int(); - Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 12), - // (2,12): error CS1525: Invalid expression term 'int' - // new scoped int(); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 12) +scoped ref R x = M; +"; + UsingTree(source, TestOptions.Script, + // (2,16): error CS1003: Syntax error, '(' expected + // scoped ref R x = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "=").WithArguments("(").WithLocation(2, 16), + // (2,16): error CS1001: Identifier expected + // scoped ref R x = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "=").WithLocation(2, 16), + // (2,19): error CS1001: Identifier expected + // scoped ref R x = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(2, 19), + // (2,19): error CS1026: ) expected + // scoped ref R x = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(2, 19) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.MethodDeclaration); { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); { - N(SyntaxKind.ObjectCreationExpression); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.NewKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.ArgumentList); - { - M(SyntaxKind.OpenParenToken); - M(SyntaxKind.CloseParenToken); - } + N(SyntaxKind.IdentifierToken, "R"); } - M(SyntaxKind.SemicolonToken); } - } - N(SyntaxKind.GlobalStatement); - { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.IdentifierToken, "x"); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.InvocationExpression); + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.ArgumentList); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierToken, "M"); } + M(SyntaxKind.IdentifierToken); } - N(SyntaxKind.SemicolonToken); + M(SyntaxKind.CloseParenToken); } + N(SyntaxKind.SemicolonToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void New_03(LanguageVersion langVersion) + [Fact] + public void Local_19() { string source = @" -new scoped S(); -"; - - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); +scoped ref readonly R x = M; +"; + UsingTree(source, TestOptions.Script, + // (2,25): error CS1003: Syntax error, '(' expected + // scoped ref readonly R x = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "=").WithArguments("(").WithLocation(2, 25), + // (2,25): error CS1001: Identifier expected + // scoped ref readonly R x = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "=").WithLocation(2, 25), + // (2,28): error CS1001: Identifier expected + // scoped ref readonly R x = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(2, 28), + // (2,28): error CS1026: ) expected + // scoped ref readonly R x = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(2, 28) + ); N(SyntaxKind.CompilationUnit); { N(SyntaxKind.MethodDeclaration); { - N(SyntaxKind.NewKeyword); - N(SyntaxKind.IdentifierName); + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "R"); + } } - N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.IdentifierToken, "x"); N(SyntaxKind.ParameterList); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.CloseParenToken); + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); } N(SyntaxKind.SemicolonToken); } @@ -3461,21 +3468,13 @@ public void New_03(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void New_04(LanguageVersion langVersion) + public void DeclExpr_01(LanguageVersion langVersion) { string source = @" -new scoped int M(); +(scoped a, var b) = M; "; - - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type - // new scoped int M(); - Diagnostic(ErrorCode.ERR_BadNewExpr, "int").WithLocation(2, 12), - // (2,12): error CS1002: ; expected - // new scoped int M(); - Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 12) - ); + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); N(SyntaxKind.CompilationUnit); { @@ -3483,35 +3482,118 @@ public void New_04(LanguageVersion langVersion) { N(SyntaxKind.ExpressionStatement); { - N(SyntaxKind.ObjectCreationExpression); + N(SyntaxKind.SimpleAssignmentExpression); { - N(SyntaxKind.NewKeyword); - N(SyntaxKind.IdentifierName); + N(SyntaxKind.TupleExpression); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); } - M(SyntaxKind.ArgumentList); + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); { - M(SyntaxKind.OpenParenToken); - M(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierToken, "M"); } } - M(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); } } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_02(LanguageVersion langVersion) + { + string source = +@" +(ref scoped b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.LocalFunctionStatement); + N(SyntaxKind.ExpressionStatement); { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.IdentifierToken, "M"); - N(SyntaxKind.ParameterList); + N(SyntaxKind.SimpleAssignmentExpression); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } } N(SyntaxKind.SemicolonToken); } @@ -3524,20 +3606,37 @@ public void New_04(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void New_05(LanguageVersion langVersion) + public void DeclExpr_03(LanguageVersion langVersion) { string source = @" -new scoped ref int M(); +(ref scoped int b, var c) = M; "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type - // new scoped ref int M(); - Diagnostic(ErrorCode.ERR_BadNewExpr, "ref").WithLocation(2, 12), - // (2,12): error CS1002: ; expected - // new scoped ref int M(); - Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(2, 12) + // (2,2): error CS1525: Invalid expression term 'ref' + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(2, 2), + // (2,13): error CS1026: ) expected + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "int").WithLocation(2, 13), + // (2,13): error CS1002: ; expected + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 13), + // (2,20): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 20), + // (2,24): error CS1003: Syntax error, ',' expected + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments(",").WithLocation(2, 24), + // (2,25): error CS1002: ; expected + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 25), + // (2,25): error CS1022: Type or namespace definition, or end-of-file expected + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 25), + // (2,27): error CS1525: Invalid expression term '=' + // (ref scoped int b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 27) ); N(SyntaxKind.CompilationUnit); @@ -3546,29 +3645,4933 @@ public void New_05(LanguageVersion langVersion) { N(SyntaxKind.ExpressionStatement); { - N(SyntaxKind.ObjectCreationExpression); + N(SyntaxKind.ParenthesizedExpression); { - N(SyntaxKind.NewKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.ArgumentList); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RefExpression); { - M(SyntaxKind.OpenParenToken); - M(SyntaxKind.CloseParenToken); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } } + M(SyntaxKind.CloseParenToken); } M(SyntaxKind.SemicolonToken); } } N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.LocalFunctionStatement); + N(SyntaxKind.LocalDeclarationStatement); { - N(SyntaxKind.RefType); + N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_04(LanguageVersion langVersion) + { + string source = +@" +(ref scoped a b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,2): error CS1525: Invalid expression term 'ref' + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(2, 2), + // (2,13): error CS1026: ) expected + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "a").WithLocation(2, 13), + // (2,13): error CS1002: ; expected + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "a").WithLocation(2, 13), + // (2,18): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 18), + // (2,22): error CS1003: Syntax error, ',' expected + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments(",").WithLocation(2, 22), + // (2,23): error CS1002: ; expected + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 23), + // (2,23): error CS1022: Type or namespace definition, or end-of-file expected + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 23), + // (2,25): error CS1525: Invalid expression term '=' + // (ref scoped a b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 25) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_05(LanguageVersion langVersion) + { + string source = +@" +(ref readonly scoped c, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_06(LanguageVersion langVersion) + { + string source = +@" +(ref readonly scoped int c, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,2): error CS1525: Invalid expression term 'ref' + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref ").WithArguments("ref").WithLocation(2, 2), + // (2,6): error CS1525: Invalid expression term 'readonly' + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(2, 6), + // (2,6): error CS1026: ) expected + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(2, 6), + // (2,6): error CS1002: ; expected + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(2, 6), + // (2,6): error CS0106: The modifier 'readonly' is not valid for this item + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(2, 6), + // (2,29): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 29), + // (2,33): error CS1003: Syntax error, ',' expected + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "d").WithArguments(",").WithLocation(2, 33), + // (2,34): error CS1002: ; expected + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 34), + // (2,34): error CS1022: Type or namespace definition, or end-of-file expected + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 34), + // (2,36): error CS1525: Invalid expression term '=' + // (ref readonly scoped int c, var d) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 36) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_07(LanguageVersion langVersion) + { + string source = +@" +(ref scoped readonly int c, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,2): error CS1525: Invalid expression term 'ref' + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(2, 2), + // (2,13): error CS1026: ) expected + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(2, 13), + // (2,13): error CS1002: ; expected + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(2, 13), + // (2,13): error CS0106: The modifier 'readonly' is not valid for this item + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(2, 13), + // (2,29): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 29), + // (2,33): error CS1003: Syntax error, ',' expected + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "d").WithArguments(",").WithLocation(2, 33), + // (2,34): error CS1002: ; expected + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 34), + // (2,34): error CS1022: Type or namespace definition, or end-of-file expected + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 34), + // (2,36): error CS1525: Invalid expression term '=' + // (ref scoped readonly int c, var d) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 36) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_08(LanguageVersion langVersion) + { + string source = +@" +(scoped int a, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_09(LanguageVersion langVersion) + { + string source = +@" +(@scoped int a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1026: ) expected + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "int").WithLocation(2, 10), + // (2,10): error CS1002: ; expected + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 10), + // (2,17): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 17), + // (2,21): error CS1003: Syntax error, ',' expected + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 21), + // (2,22): error CS1002: ; expected + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 22), + // (2,22): error CS1022: Type or namespace definition, or end-of-file expected + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 22), + // (2,24): error CS1525: Invalid expression term '=' + // (@scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 24) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_10(LanguageVersion langVersion) + { + string source = +@" +(scoped ref int b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_11(LanguageVersion langVersion) + { + string source = +@" +(@scoped ref int b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1026: ) expected + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "ref").WithLocation(2, 10), + // (2,10): error CS1002: ; expected + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(2, 10), + // (2,21): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 21), + // (2,25): error CS1003: Syntax error, ',' expected + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments(",").WithLocation(2, 25), + // (2,26): error CS1002: ; expected + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 26), + // (2,26): error CS1022: Type or namespace definition, or end-of-file expected + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 26), + // (2,28): error CS1525: Invalid expression term '=' + // (@scoped ref int b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 28) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_12(LanguageVersion langVersion) + { + string source = +@" +(scoped ref readonly int a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_13(LanguageVersion langVersion) + { + string source = +@" +(@scoped ref readonly int a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1026: ) expected + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "ref").WithLocation(2, 10), + // (2,10): error CS1002: ; expected + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(2, 10), + // (2,30): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 30), + // (2,34): error CS1003: Syntax error, ',' expected + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 34), + // (2,35): error CS1002: ; expected + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 35), + // (2,35): error CS1022: Type or namespace definition, or end-of-file expected + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 35), + // (2,37): error CS1525: Invalid expression term '=' + // (@scoped ref readonly int a, var b) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 37) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_14(LanguageVersion langVersion) + { + string source = +@" +(scoped S a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_15(LanguageVersion langVersion) + { + string source = +@" +(scoped ref S b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_16(LanguageVersion langVersion) + { + string source = +@" +(scoped ref readonly S a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_17(LanguageVersion langVersion) + { + string source = +@" +(scoped.nested a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "nested"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_18(LanguageVersion langVersion) + { + string source = +@" +(scoped scoped a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_20(LanguageVersion langVersion) + { + string source = +@" +(scoped var a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_21(LanguageVersion langVersion) + { + string source = +@" +(scoped ref var b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_22(LanguageVersion langVersion) + { + string source = +@" +(scoped ref readonly var c, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_23(LanguageVersion langVersion) + { + string source = +@" +(scoped var, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_24(LanguageVersion langVersion) + { + string source = +@" +(ref scoped var, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_25(LanguageVersion langVersion) + { + string source = +@" +(scoped scoped int a, var b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,9): error CS1026: ) expected + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(2, 9), + // (2,9): error CS1002: ; expected + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "scoped").WithLocation(2, 9), + // (2,23): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 23), + // (2,27): error CS1003: Syntax error, ',' expected + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 27), + // (2,28): error CS1002: ; expected + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 28), + // (2,28): error CS1022: Type or namespace definition, or end-of-file expected + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 28), + // (2,30): error CS1525: Invalid expression term '=' + // (scoped scoped int a, var b) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 30) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_26(LanguageVersion langVersion) + { + string source = +@" +(scoped scoped var b, var c) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,9): error CS1026: ) expected + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(2, 9), + // (2,9): error CS1002: ; expected + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "scoped").WithLocation(2, 9), + // (2,23): error CS1044: Cannot use more than one type in a for, using, fixed, or declaration statement + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_MultiTypeInDeclaration, "var").WithLocation(2, 23), + // (2,27): error CS1003: Syntax error, ',' expected + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_SyntaxError, "c").WithArguments(",").WithLocation(2, 27), + // (2,28): error CS1002: ; expected + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 28), + // (2,28): error CS1022: Type or namespace definition, or end-of-file expected + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 28), + // (2,30): error CS1525: Invalid expression term '=' + // (scoped scoped var b, var c) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 30) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_27(LanguageVersion langVersion) + { + string source = +@" +scoped var (a, b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,14): error CS1001: Identifier expected + // scoped var (a, b) = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(2, 14), + // (2,17): error CS1001: Identifier expected + // scoped var (a, b) = M; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(2, 17), + // (2,19): error CS1002: ; expected + // scoped var (a, b) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "=").WithLocation(2, 19), + // (2,19): error CS1525: Invalid expression term '=' + // scoped var (a, b) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 19) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "var"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_28(LanguageVersion langVersion) + { + string source = +@" +scoped ref var (a, b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,12): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // scoped ref var (a, b) = M; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "var").WithLocation(2, 12) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_29(LanguageVersion langVersion) + { + string source = +@" +scoped ref readonly var (a, b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,21): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // scoped ref readonly var (a, b) = M; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "var").WithLocation(2, 21) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_30(LanguageVersion langVersion) + { + string source = +@" +(name: scoped int a, var d) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "name"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_31(LanguageVersion langVersion) + { + string source = +@" +(var a, scoped int b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_32(LanguageVersion langVersion) + { + string source = +@" +(var a, name: scoped int b) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "name"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void DeclExpr_33(LanguageVersion langVersion) + { + string source = +@" +(var a, scoped var (b, c)) = M; +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,16): error CS1026: ) expected + // (var a, scoped var (b, c)) = M; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "var").WithLocation(2, 16), + // (2,16): error CS1002: ; expected + // (var a, scoped var (b, c)) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "var").WithLocation(2, 16), + // (2,26): error CS1002: ; expected + // (var a, scoped var (b, c)) = M; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 26), + // (2,26): error CS1022: Type or namespace definition, or end-of-file expected + // (var a, scoped var (b, c)) = M; + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 26), + // (2,28): error CS1525: Invalid expression term '=' + // (var a, scoped var (b, c)) = M; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(2, 28) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_01(LanguageVersion langVersion) + { + string source = +@" +M(out scoped a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_02(LanguageVersion langVersion) + { + string source = +@" +M(out ref scoped b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_03(LanguageVersion langVersion) + { + string source = +@" +M(out ref scoped int b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,7): error CS1525: Invalid expression term 'ref' + // M(out ref scoped int b); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(2, 7), + // (2,18): error CS1003: Syntax error, ',' expected + // M(out ref scoped int b); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(2, 18), + // (2,18): error CS1525: Invalid expression term 'int' + // M(out ref scoped int b); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 18), + // (2,22): error CS1003: Syntax error, ',' expected + // M(out ref scoped int b); + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 22) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_04(LanguageVersion langVersion) + { + string source = +@" +M(out ref scoped a b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,20): error CS1003: Syntax error, ',' expected + // M(out ref scoped a b); + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 20) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_05(LanguageVersion langVersion) + { + string source = +@" +M(out ref readonly scoped c); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_06(LanguageVersion langVersion) + { + string source = +@" +M(out ref readonly scoped int c); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,7): error CS1525: Invalid expression term 'ref' + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref ").WithArguments("ref").WithLocation(2, 7), + // (2,11): error CS1525: Invalid expression term 'readonly' + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(2, 11), + // (2,11): error CS1026: ) expected + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(2, 11), + // (2,11): error CS1002: ; expected + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(2, 11), + // (2,11): error CS0106: The modifier 'readonly' is not valid for this item + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(2, 11), + // (2,32): error CS1002: ; expected + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 32), + // (2,32): error CS1022: Type or namespace definition, or end-of-file expected + // M(out ref readonly scoped int c); + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 32) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_07(LanguageVersion langVersion) + { + string source = +@" +M(out ref scoped readonly int c); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,7): error CS1525: Invalid expression term 'ref' + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(2, 7), + // (2,18): error CS1026: ) expected + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(2, 18), + // (2,18): error CS1002: ; expected + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(2, 18), + // (2,18): error CS0106: The modifier 'readonly' is not valid for this item + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(2, 18), + // (2,32): error CS1002: ; expected + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 32), + // (2,32): error CS1022: Type or namespace definition, or end-of-file expected + // M(out ref scoped readonly int c); + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 32) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + } + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_08(LanguageVersion langVersion) + { + string source = +@" +M(out scoped int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_09(LanguageVersion langVersion) + { + string source = +@" +M(out @scoped int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,15): error CS1003: Syntax error, ',' expected + // M(out @scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(2, 15), + // (2,15): error CS1525: Invalid expression term 'int' + // M(out @scoped int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 15), + // (2,19): error CS1003: Syntax error, ',' expected + // M(out @scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 19) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_10(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref int b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_11(LanguageVersion langVersion) + { + string source = +@" +M(out @scoped ref int b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,15): error CS1003: Syntax error, ',' expected + // M(out @scoped ref int b); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 15), + // (2,19): error CS1525: Invalid expression term 'int' + // M(out @scoped ref int b); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 19), + // (2,23): error CS1003: Syntax error, ',' expected + // M(out @scoped ref int b); + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 23) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_12(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref readonly int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_13(LanguageVersion langVersion) + { + string source = +@" +M(out @scoped ref readonly int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,15): error CS1003: Syntax error, ',' expected + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 15), + // (2,19): error CS1525: Invalid expression term 'readonly' + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(2, 19), + // (2,19): error CS1026: ) expected + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(2, 19), + // (2,19): error CS1002: ; expected + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(2, 19), + // (2,19): error CS0106: The modifier 'readonly' is not valid for this item + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(2, 19), + // (2,33): error CS1002: ; expected + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 33), + // (2,33): error CS1022: Type or namespace definition, or end-of-file expected + // M(out @scoped ref readonly int a); + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 33) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_14(LanguageVersion langVersion) + { + string source = +@" +M(out scoped S a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_15(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref S b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_16(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref readonly S a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_17(LanguageVersion langVersion) + { + string source = +@" +M(out scoped.nested a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "nested"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_18(LanguageVersion langVersion) + { + string source = +@" +M(out scoped scoped a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_20(LanguageVersion langVersion) + { + string source = +@" +M(out scoped var a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_21(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref var b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_22(LanguageVersion langVersion) + { + string source = +@" +M(out scoped ref readonly var c); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_23(LanguageVersion langVersion) + { + string source = +@" +M(out scoped var); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_24(LanguageVersion langVersion) + { + string source = +@" +M(out ref scoped var); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_25(LanguageVersion langVersion) + { + string source = +@" +M(out scoped scoped int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,21): error CS1003: Syntax error, ',' expected + // M(out scoped scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(2, 21), + // (2,21): error CS1525: Invalid expression term 'int' + // M(out scoped scoped int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 21), + // (2,25): error CS1003: Syntax error, ',' expected + // M(out scoped scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 25) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_26(LanguageVersion langVersion) + { + string source = +@" +M(out scoped scoped var b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,25): error CS1003: Syntax error, ',' expected + // M(out scoped scoped var b); + Diagnostic(ErrorCode.ERR_SyntaxError, "b").WithArguments(",").WithLocation(2, 25) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_27(LanguageVersion langVersion) + { + string source = +@" +M(ref out scoped int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,7): error CS1525: Invalid expression term 'out' + // M(ref out scoped int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(2, 7), + // (2,7): error CS1003: Syntax error, ',' expected + // M(ref out scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",").WithLocation(2, 7) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_28(LanguageVersion langVersion) + { + string source = +@" +M(scoped int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1003: Syntax error, ',' expected + // M(scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(2, 10), + // (2,10): error CS1525: Invalid expression term 'int' + // M(scoped int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 10), + // (2,14): error CS1003: Syntax error, ',' expected + // M(scoped int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 14) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_29(LanguageVersion langVersion) + { + string source = +@" +M(scoped ref int a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1003: Syntax error, ',' expected + // M(scoped ref int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 10), + // (2,14): error CS1525: Invalid expression term 'int' + // M(scoped ref int a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 14), + // (2,18): error CS1003: Syntax error, ',' expected + // M(scoped ref int a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 18) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_30(LanguageVersion langVersion) + { + string source = +@" +M(ref out scoped S a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,7): error CS1525: Invalid expression term 'out' + // M(ref out scoped S a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(2, 7), + // (2,7): error CS1003: Syntax error, ',' expected + // M(ref out scoped S a); + Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",").WithLocation(2, 7) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_31(LanguageVersion langVersion) + { + string source = +@" +M(scoped S a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1003: Syntax error, ',' expected + // M(scoped S a); + Diagnostic(ErrorCode.ERR_SyntaxError, "S").WithArguments(",").WithLocation(2, 10), + // (2,12): error CS1003: Syntax error, ',' expected + // M(scoped S a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 12) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_32(LanguageVersion langVersion) + { + string source = +@" +M(scoped ref S a); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,10): error CS1003: Syntax error, ',' expected + // M(scoped ref S a); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 10), + // (2,16): error CS1003: Syntax error, ',' expected + // M(scoped ref S a); + Diagnostic(ErrorCode.ERR_SyntaxError, "a").WithArguments(",").WithLocation(2, 16) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_33(LanguageVersion langVersion) + { + string source = +@" +M(out scoped var _); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.DiscardDesignation); + { + N(SyntaxKind.UnderscoreToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void OutDeclExpr_34(LanguageVersion langVersion) + { + string source = +@" +M(out scoped _); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.OutKeyword); + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.DiscardDesignation); + { + N(SyntaxKind.UnderscoreToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void New_01(LanguageVersion langVersion) + { + string source = +@" +new scoped(); +"; + + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void New_02(LanguageVersion langVersion) + { + string source = +@" +new scoped int(); +"; + + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type + // new scoped int(); + Diagnostic(ErrorCode.ERR_BadNewExpr, "int").WithLocation(2, 12), + // (2,12): error CS1002: ; expected + // new scoped int(); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 12), + // (2,12): error CS1525: Invalid expression term 'int' + // new scoped int(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(2, 12) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.ArgumentList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void New_03(LanguageVersion langVersion) + { + string source = +@" +new scoped S(); +"; + + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void New_04(LanguageVersion langVersion) + { + string source = +@" +new scoped int M(); +"; + + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type + // new scoped int M(); + Diagnostic(ErrorCode.ERR_BadNewExpr, "int").WithLocation(2, 12), + // (2,12): error CS1002: ; expected + // new scoped int M(); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(2, 12) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.ArgumentList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void New_05(LanguageVersion langVersion) + { + string source = +@" +new scoped ref int M(); +"; + + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,12): error CS1526: A new expression requires an argument list or (), [], or {} after type + // new scoped ref int M(); + Diagnostic(ErrorCode.ERR_BadNewExpr, "ref").WithLocation(2, 12), + // (2,12): error CS1002: ; expected + // new scoped ref int M(); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(2, 12) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.ArgumentList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); N(SyntaxKind.PredefinedType); { N(SyntaxKind.IntKeyword); @@ -8563,12 +13566,201 @@ public void UsingStmt_20(LanguageVersion langVersion) N(SyntaxKind.ScopedKeyword); N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken, "var"); + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void UsingStmt_21(LanguageVersion langVersion) + { + string source = +@" +using (scoped ref var b); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.UsingStatement); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void UsingStmt_22(LanguageVersion langVersion) + { + string source = +@" +using (scoped ref readonly var c); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.UsingStatement); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void UsingStmt_23(LanguageVersion langVersion) + { + string source = +@" +using (scoped var); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.UsingStatement); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "var"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void UsingStmt_24(LanguageVersion langVersion) + { + string source = +@" +using (ref scoped var); +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.UsingStatement); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); } } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.IdentifierToken, "var"); } } N(SyntaxKind.CloseParenToken); @@ -8586,13 +13778,23 @@ public void UsingStmt_20(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_21(LanguageVersion langVersion) + public void UsingStmt_25(LanguageVersion langVersion) { string source = @" -using (scoped ref var b); +using (scoped scoped int a); "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,22): error CS1026: ) expected + // using (scoped scoped int a); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "int").WithLocation(2, 22), + // (2,27): error CS1002: ; expected + // using (scoped scoped int a); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 27), + // (2,27): error CS1022: Type or namespace definition, or end-of-file expected + // using (scoped scoped int a); + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 27) + ); N(SyntaxKind.CompilationUnit); { @@ -8604,30 +13806,40 @@ public void UsingStmt_21(LanguageVersion langVersion) N(SyntaxKind.OpenParenToken); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ScopedType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.RefType); - { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "var"); - } - } + N(SyntaxKind.IdentifierToken, "scoped"); } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "b"); + N(SyntaxKind.IdentifierToken, "scoped"); } } - N(SyntaxKind.CloseParenToken); - N(SyntaxKind.EmptyStatement); + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.LocalDeclarationStatement); { - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + M(SyntaxKind.SemicolonToken); } } } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } N(SyntaxKind.EndOfFileToken); } EOF(); @@ -8636,13 +13848,23 @@ public void UsingStmt_21(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_22(LanguageVersion langVersion) + public void UsingStmt_26(LanguageVersion langVersion) { string source = @" -using (scoped ref readonly var c); +using (scoped scoped var b); "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (2,26): error CS1026: ) expected + // using (scoped scoped var b); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "b").WithLocation(2, 26), + // (2,27): error CS1002: ; expected + // using (scoped scoped var b); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 27), + // (2,27): error CS1022: Type or namespace definition, or end-of-file expected + // using (scoped scoped var b); + Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 27) + ); N(SyntaxKind.CompilationUnit); { @@ -8657,28 +13879,34 @@ public void UsingStmt_22(LanguageVersion langVersion) N(SyntaxKind.ScopedType); { N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.RefType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.ReadOnlyKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "var"); - } + N(SyntaxKind.IdentifierToken, "scoped"); } } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "c"); + N(SyntaxKind.IdentifierToken, "var"); } } - N(SyntaxKind.CloseParenToken); - N(SyntaxKind.EmptyStatement); + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.ExpressionStatement); { - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + M(SyntaxKind.SemicolonToken); } } } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } N(SyntaxKind.EndOfFileToken); } EOF(); @@ -8687,39 +13915,49 @@ public void UsingStmt_22(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_23(LanguageVersion langVersion) + public void Field_01(LanguageVersion langVersion) { string source = @" -using (scoped var); +ref struct R2 +{ + scoped ref int F3; +} "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.StructDeclaration); { - N(SyntaxKind.UsingStatement); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "R2"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.UsingKeyword); - N(SyntaxKind.OpenParenToken); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.ScopedType); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "var"); + N(SyntaxKind.IdentifierToken, "F3"); } } - N(SyntaxKind.CloseParenToken); - N(SyntaxKind.EmptyStatement); - { - N(SyntaxKind.SemicolonToken); - } + N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } @@ -8729,43 +13967,67 @@ public void UsingStmt_23(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_24(LanguageVersion langVersion) + public void Field_02(LanguageVersion langVersion) { string source = @" -using (ref scoped var); +ref struct R2 +{ + const scoped int F3; +} "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (4,18): error CS1001: Identifier expected + // const scoped int F3; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), + // (4,18): error CS0145: A const field requires a value to be provided + // const scoped int F3; + Diagnostic(ErrorCode.ERR_ConstValueRequired, "int").WithLocation(4, 18), + // (4,18): error CS1002: ; expected + // const scoped int F3; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(4, 18) + ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.StructDeclaration); { - N(SyntaxKind.UsingStatement); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "R2"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.UsingKeyword); - N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConstKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.RefType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } + N(SyntaxKind.IdentifierToken, "scoped"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "var"); + M(SyntaxKind.IdentifierToken); } } - N(SyntaxKind.CloseParenToken); - N(SyntaxKind.EmptyStatement); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F3"); + } } + N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } @@ -8775,67 +14037,71 @@ public void UsingStmt_24(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_25(LanguageVersion langVersion) + public void Field_03(LanguageVersion langVersion) { string source = @" -using (scoped scoped int a); +ref struct R2 +{ + const scoped ref int F3; +} "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (2,22): error CS1026: ) expected - // using (scoped scoped int a); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "int").WithLocation(2, 22), - // (2,27): error CS1002: ; expected - // using (scoped scoped int a); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 27), - // (2,27): error CS1022: Type or namespace definition, or end-of-file expected - // using (scoped scoped int a); - Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 27) + // (4,18): error CS1001: Identifier expected + // const scoped ref int F3; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), + // (4,18): error CS0145: A const field requires a value to be provided + // const scoped ref int F3; + Diagnostic(ErrorCode.ERR_ConstValueRequired, "ref").WithLocation(4, 18), + // (4,18): error CS1002: ; expected + // const scoped ref int F3; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(4, 18) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.StructDeclaration); { - N(SyntaxKind.UsingStatement); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "R2"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.UsingKeyword); - N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConstKeyword); N(SyntaxKind.VariableDeclaration); { N(SyntaxKind.IdentifierName); { N(SyntaxKind.IdentifierToken, "scoped"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "scoped"); + M(SyntaxKind.IdentifierToken); } } - M(SyntaxKind.CloseParenToken); - N(SyntaxKind.LocalDeclarationStatement); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.RefType); { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.IntKeyword); } } - M(SyntaxKind.SemicolonToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F3"); + } } - } - } - N(SyntaxKind.GlobalStatement); - { - N(SyntaxKind.EmptyStatement); - { N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } @@ -8845,64 +14111,107 @@ public void UsingStmt_25(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void UsingStmt_26(LanguageVersion langVersion) + public void Field_04(LanguageVersion langVersion) { string source = @" -using (scoped scoped var b); +ref struct R2 +{ + fixed scoped int F3[2]; +} "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (2,26): error CS1026: ) expected - // using (scoped scoped var b); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "b").WithLocation(2, 26), - // (2,27): error CS1002: ; expected - // using (scoped scoped var b); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(2, 27), - // (2,27): error CS1022: Type or namespace definition, or end-of-file expected - // using (scoped scoped var b); - Diagnostic(ErrorCode.ERR_EOFExpected, ")").WithLocation(2, 27) + // (4,18): error CS1001: Identifier expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), + // (4,18): error CS1003: Syntax error, '[' expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("[").WithLocation(4, 18), + // (4,18): error CS1525: Invalid expression term 'int' + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(4, 18), + // (4,22): error CS1003: Syntax error, ',' expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, "F3").WithArguments(",").WithLocation(4, 22), + // (4,27): error CS1003: Syntax error, ',' expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(4, 27), + // (4,27): error CS0443: Syntax error; value expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_ValueExpected, "").WithLocation(4, 27), + // (4,27): error CS1003: Syntax error, ']' expected + // fixed scoped int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]").WithLocation(4, 27) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.StructDeclaration); { - N(SyntaxKind.UsingStatement); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "R2"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.UsingKeyword); - N(SyntaxKind.OpenParenToken); + N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ScopedType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } + N(SyntaxKind.IdentifierToken, "scoped"); } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "var"); - } - } - M(SyntaxKind.CloseParenToken); - N(SyntaxKind.ExpressionStatement); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "b"); + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.BracketedArgumentList); + { + M(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F3"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.Argument); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseBracketToken); + } } - M(SyntaxKind.SemicolonToken); } - } - } - N(SyntaxKind.GlobalStatement); - { - N(SyntaxKind.EmptyStatement); - { N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } @@ -8912,16 +14221,41 @@ public void UsingStmt_26(LanguageVersion langVersion) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_01(LanguageVersion langVersion) + public void Field_05(LanguageVersion langVersion) { string source = @" ref struct R2 { - scoped ref int F3; + fixed scoped ref int F3[2]; } "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), + // (4,18): error CS1001: Identifier expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), + // (4,18): error CS1003: Syntax error, '[' expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments("[").WithLocation(4, 18), + // (4,18): error CS1525: Invalid expression term 'ref' + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref int").WithArguments("ref").WithLocation(4, 18), + // (4,22): error CS1525: Invalid expression term 'int' + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(4, 22), + // (4,26): error CS1003: Syntax error, ',' expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, "F3").WithArguments(",").WithLocation(4, 26), + // (4,31): error CS1003: Syntax error, ',' expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(4, 31), + // (4,31): error CS0443: Syntax error; value expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_ValueExpected, "").WithLocation(4, 31), + // (4,31): error CS1003: Syntax error, ']' expected + // fixed scoped ref int F3[2]; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]").WithLocation(4, 31) + ); N(SyntaxKind.CompilationUnit); { @@ -8933,24 +14267,64 @@ ref struct R2 N(SyntaxKind.OpenBraceToken); N(SyntaxKind.FieldDeclaration); { + N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ScopedType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.RefType); + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.BracketedArgumentList); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.PredefinedType); + M(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F3"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.Argument); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } } + M(SyntaxKind.CloseBracketToken); } } - N(SyntaxKind.VariableDeclarator); - { - N(SyntaxKind.IdentifierToken, "F3"); - } } N(SyntaxKind.SemicolonToken); } @@ -8964,25 +14338,22 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_02(LanguageVersion langVersion) + public void Field_06(LanguageVersion langVersion) { string source = @" ref struct R2 { - const scoped int F3; + scoped const int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // const scoped int F3; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), - // (4,18): error CS0145: A const field requires a value to be provided - // const scoped int F3; - Diagnostic(ErrorCode.ERR_ConstValueRequired, "int").WithLocation(4, 18), - // (4,18): error CS1002: ; expected - // const scoped int F3; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "int").WithLocation(4, 18) + // (4,12): error CS1519: Invalid token 'const' in class, record, struct, or interface member declaration + // scoped const int F3; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "const").WithArguments("const").WithLocation(4, 12), + // (4,22): error CS0145: A const field requires a value to be provided + // scoped const int F3; + Diagnostic(ErrorCode.ERR_ConstValueRequired, "F3").WithLocation(4, 22) ); N(SyntaxKind.CompilationUnit); @@ -8993,24 +14364,16 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.ConstKeyword); - N(SyntaxKind.VariableDeclaration); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.VariableDeclarator); - { - M(SyntaxKind.IdentifierToken); - } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); } - M(SyntaxKind.SemicolonToken); } N(SyntaxKind.FieldDeclaration); { + N(SyntaxKind.ConstKeyword); N(SyntaxKind.VariableDeclaration); { N(SyntaxKind.PredefinedType); @@ -9034,25 +14397,22 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_03(LanguageVersion langVersion) + public void Field_07(LanguageVersion langVersion) { string source = @" ref struct R2 { - const scoped ref int F3; + scoped ref const int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // const scoped ref int F3; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), - // (4,18): error CS0145: A const field requires a value to be provided - // const scoped ref int F3; - Diagnostic(ErrorCode.ERR_ConstValueRequired, "ref").WithLocation(4, 18), - // (4,18): error CS1002: ; expected - // const scoped ref int F3; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "ref").WithLocation(4, 18) + // (4,16): error CS1031: Type expected + // scoped ref const int F3; + Diagnostic(ErrorCode.ERR_TypeExpected, "const").WithLocation(4, 16), + // (4,26): error CS0145: A const field requires a value to be provided + // scoped ref const int F3; + Diagnostic(ErrorCode.ERR_ConstValueRequired, "F3").WithLocation(4, 26) ); N(SyntaxKind.CompilationUnit); @@ -9063,33 +14423,26 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.IncompleteMember); { - N(SyntaxKind.ConstKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.VariableDeclarator); + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); { M(SyntaxKind.IdentifierToken); } } - M(SyntaxKind.SemicolonToken); } N(SyntaxKind.FieldDeclaration); { + N(SyntaxKind.ConstKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.RefType); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } + N(SyntaxKind.IntKeyword); } N(SyntaxKind.VariableDeclarator); { @@ -9108,37 +14461,19 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_04(LanguageVersion langVersion) + public void Field_08(LanguageVersion langVersion) { string source = @" ref struct R2 { - fixed scoped int F3[2]; + scoped fixed int F3[2]; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), - // (4,18): error CS1003: Syntax error, '[' expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("[").WithLocation(4, 18), - // (4,18): error CS1525: Invalid expression term 'int' - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(4, 18), - // (4,22): error CS1003: Syntax error, ',' expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, "F3").WithArguments(",").WithLocation(4, 22), - // (4,27): error CS1003: Syntax error, ',' expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(4, 27), - // (4,27): error CS0443: Syntax error; value expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_ValueExpected, "").WithLocation(4, 27), - // (4,27): error CS1003: Syntax error, ']' expected - // fixed scoped int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]").WithLocation(4, 27) + // (4,12): error CS1519: Invalid token 'fixed' in class, record, struct, or interface member declaration + // scoped fixed int F3[2]; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "fixed").WithArguments("fixed").WithLocation(4, 12) ); N(SyntaxKind.CompilationUnit); @@ -9149,60 +14484,36 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } N(SyntaxKind.FieldDeclaration); { N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.IntKeyword); } N(SyntaxKind.VariableDeclarator); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "F3"); N(SyntaxKind.BracketedArgumentList); { - M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - M(SyntaxKind.CommaToken); + N(SyntaxKind.OpenBracketToken); N(SyntaxKind.Argument); { - N(SyntaxKind.ElementAccessExpression); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "F3"); - } - N(SyntaxKind.BracketedArgumentList); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.NumericLiteralExpression); - { - N(SyntaxKind.NumericLiteralToken, "2"); - } - } - N(SyntaxKind.CloseBracketToken); - } - } - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.Argument); - { - M(SyntaxKind.IdentifierName); + N(SyntaxKind.NumericLiteralExpression); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.NumericLiteralToken, "2"); } } - M(SyntaxKind.CloseBracketToken); + N(SyntaxKind.CloseBracketToken); } } } @@ -9218,40 +14529,19 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_05(LanguageVersion langVersion) + public void Field_09(LanguageVersion langVersion) { string source = @" ref struct R2 { - fixed scoped ref int F3[2]; + scoped ref fixed int F3[2]; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), - // (4,18): error CS1003: Syntax error, '[' expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments("[").WithLocation(4, 18), - // (4,18): error CS1525: Invalid expression term 'ref' - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref int").WithArguments("ref").WithLocation(4, 18), - // (4,22): error CS1525: Invalid expression term 'int' - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(4, 22), - // (4,26): error CS1003: Syntax error, ',' expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, "F3").WithArguments(",").WithLocation(4, 26), - // (4,31): error CS1003: Syntax error, ',' expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(4, 31), - // (4,31): error CS0443: Syntax error; value expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_ValueExpected, "").WithLocation(4, 31), - // (4,31): error CS1003: Syntax error, ']' expected - // fixed scoped ref int F3[2]; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("]").WithLocation(4, 31) + // (4,16): error CS1031: Type expected + // scoped ref fixed int F3[2]; + Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(4, 16) ); N(SyntaxKind.CompilationUnit); @@ -9262,66 +14552,91 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } N(SyntaxKind.FieldDeclaration); { N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.IntKeyword); } N(SyntaxKind.VariableDeclarator); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "F3"); N(SyntaxKind.BracketedArgumentList); { - M(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.RefExpression); - { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - } - M(SyntaxKind.CommaToken); + N(SyntaxKind.OpenBracketToken); N(SyntaxKind.Argument); { - N(SyntaxKind.ElementAccessExpression); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "F3"); - } - N(SyntaxKind.BracketedArgumentList); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.NumericLiteralExpression); - { - N(SyntaxKind.NumericLiteralToken, "2"); - } - } - N(SyntaxKind.CloseBracketToken); - } - } - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.Argument); - { - M(SyntaxKind.IdentifierName); + N(SyntaxKind.NumericLiteralExpression); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.NumericLiteralToken, "2"); } } - M(SyntaxKind.CloseBracketToken); + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void Field_10(LanguageVersion langVersion) + { + string source = +@" +class C +{ + scoped record A; +} +"; + UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.ScopedType); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "record"); } } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "A"); + } } N(SyntaxKind.SemicolonToken); } @@ -9335,22 +14650,23 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_06(LanguageVersion langVersion) + public void Field_11(LanguageVersion langVersion) { string source = @" ref struct R2 { - scoped const int F3; + scoped private R1 F1; + scoped private ref int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,12): error CS1519: Invalid token 'const' in class, record, struct, or interface member declaration - // scoped const int F3; - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "const").WithArguments("const").WithLocation(4, 12), - // (4,22): error CS0145: A const field requires a value to be provided - // scoped const int F3; - Diagnostic(ErrorCode.ERR_ConstValueRequired, "F3").WithLocation(4, 22) + // (4,12): error CS1585: Member modifier 'private' must precede the member type and name + // scoped private R1 F1; + Diagnostic(ErrorCode.ERR_BadModifierLocation, "private").WithArguments("private").WithLocation(4, 12), + // (5,12): error CS1585: Member modifier 'private' must precede the member type and name + // scoped private ref int F3; + Diagnostic(ErrorCode.ERR_BadModifierLocation, "private").WithArguments("private").WithLocation(5, 12) ); N(SyntaxKind.CompilationUnit); @@ -9370,12 +14686,39 @@ ref struct R2 } N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.ConstKeyword); + N(SyntaxKind.PrivateKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierToken, "R1"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F1"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.PrivateKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } } N(SyntaxKind.VariableDeclarator); { @@ -9394,22 +14737,19 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_07(LanguageVersion langVersion) + public void Event_01(LanguageVersion langVersion) { string source = @" ref struct R2 { - scoped ref const int F3; + scoped event int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,16): error CS1031: Type expected - // scoped ref const int F3; - Diagnostic(ErrorCode.ERR_TypeExpected, "const").WithLocation(4, 16), - // (4,26): error CS0145: A const field requires a value to be provided - // scoped ref const int F3; - Diagnostic(ErrorCode.ERR_ConstValueRequired, "F3").WithLocation(4, 26) + // (4,12): error CS1519: Invalid token 'event' in class, record, struct, or interface member declaration + // scoped event int F3; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "event").WithArguments("event").WithLocation(4, 12) ); N(SyntaxKind.CompilationUnit); @@ -9422,19 +14762,14 @@ ref struct R2 N(SyntaxKind.OpenBraceToken); N(SyntaxKind.IncompleteMember); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.RefType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.RefKeyword); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } + N(SyntaxKind.IdentifierToken, "scoped"); } } - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.EventFieldDeclaration); { - N(SyntaxKind.ConstKeyword); + N(SyntaxKind.EventKeyword); N(SyntaxKind.VariableDeclaration); { N(SyntaxKind.PredefinedType); @@ -9458,19 +14793,25 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_08(LanguageVersion langVersion) + public void Event_02(LanguageVersion langVersion) { string source = @" ref struct R2 { - scoped fixed int F3[2]; + event scoped int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,12): error CS1519: Invalid token 'fixed' in class, record, struct, or interface member declaration - // scoped fixed int F3[2]; - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "fixed").WithArguments("fixed").WithLocation(4, 12) + // (4,18): error CS1001: Identifier expected + // event scoped int F3; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), + // (4,18): error CS1514: { expected + // event scoped int F3; + Diagnostic(ErrorCode.ERR_LbraceExpected, "int").WithLocation(4, 18), + // (4,18): error CS1513: } expected + // event scoped int F3; + Diagnostic(ErrorCode.ERR_RbraceExpected, "int").WithLocation(4, 18) ); N(SyntaxKind.CompilationUnit); @@ -9481,16 +14822,22 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); + N(SyntaxKind.EventDeclaration); { + N(SyntaxKind.EventKeyword); N(SyntaxKind.IdentifierName); { N(SyntaxKind.IdentifierToken, "scoped"); } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.AccessorList); + { + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } } N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { N(SyntaxKind.PredefinedType); @@ -9500,18 +14847,6 @@ ref struct R2 N(SyntaxKind.VariableDeclarator); { N(SyntaxKind.IdentifierToken, "F3"); - N(SyntaxKind.BracketedArgumentList); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.NumericLiteralExpression); - { - N(SyntaxKind.NumericLiteralToken, "2"); - } - } - N(SyntaxKind.CloseBracketToken); - } } } N(SyntaxKind.SemicolonToken); @@ -9526,19 +14861,25 @@ ref struct R2 [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] - public void Field_09(LanguageVersion langVersion) + public void Event_03(LanguageVersion langVersion) { string source = @" ref struct R2 { - scoped ref fixed int F3[2]; + event scoped ref int F3; } "; UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,16): error CS1031: Type expected - // scoped ref fixed int F3[2]; - Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(4, 16) + // (4,18): error CS1001: Identifier expected + // event scoped ref int F3; + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), + // (4,18): error CS1514: { expected + // event scoped ref int F3; + Diagnostic(ErrorCode.ERR_LbraceExpected, "ref").WithLocation(4, 18), + // (4,18): error CS1513: } expected + // event scoped ref int F3; + Diagnostic(ErrorCode.ERR_RbraceExpected, "ref").WithLocation(4, 18) ); N(SyntaxKind.CompilationUnit); @@ -9549,42 +14890,35 @@ ref struct R2 N(SyntaxKind.StructKeyword); N(SyntaxKind.IdentifierToken, "R2"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); + N(SyntaxKind.EventDeclaration); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.RefType); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.RefKeyword); - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.AccessorList); + { + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); } } N(SyntaxKind.FieldDeclaration); { - N(SyntaxKind.FixedKeyword); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.RefType); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } } N(SyntaxKind.VariableDeclarator); { N(SyntaxKind.IdentifierToken, "F3"); - N(SyntaxKind.BracketedArgumentList); - { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.NumericLiteralExpression); - { - N(SyntaxKind.NumericLiteralToken, "2"); - } - } - N(SyntaxKind.CloseBracketToken); - } } } N(SyntaxKind.SemicolonToken); @@ -9596,331 +14930,393 @@ ref struct R2 EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp8)] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void Field_10(LanguageVersion langVersion) + [Fact] + public void Fixed_01() { string source = @" -class C -{ - scoped record A; -} +fixed (scoped int* a = b); "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion)); + UsingTree(source, + // (2,15): error CS1001: Identifier expected + // fixed (scoped int* a = b); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(2, 15), + // (2,15): error CS1003: Syntax error, ',' expected + // fixed (scoped int* a = b); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(2, 15) + ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.ClassDeclaration); + N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.ClassKeyword); - N(SyntaxKind.IdentifierToken, "C"); - N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.FixedStatement); { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.OpenParenToken); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ScopedType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.ScopedKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "record"); - } + N(SyntaxKind.IdentifierToken, "scoped"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "A"); + M(SyntaxKind.IdentifierToken); } } - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } } - N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void Field_11(LanguageVersion langVersion) + [Fact] + public void Fixed_02() { string source = @" -ref struct R2 -{ - scoped private R1 F1; - scoped private ref int F3; -} +fixed (scoped ref int* a = b); "; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,12): error CS1585: Member modifier 'private' must precede the member type and name - // scoped private R1 F1; - Diagnostic(ErrorCode.ERR_BadModifierLocation, "private").WithArguments("private").WithLocation(4, 12), - // (5,12): error CS1585: Member modifier 'private' must precede the member type and name - // scoped private ref int F3; - Diagnostic(ErrorCode.ERR_BadModifierLocation, "private").WithArguments("private").WithLocation(5, 12) + UsingTree(source, + // (2,15): error CS1001: Identifier expected + // fixed (scoped ref int* a = b); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(2, 15), + // (2,15): error CS1003: Syntax error, ',' expected + // fixed (scoped ref int* a = b); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 15) ); - N(SyntaxKind.CompilationUnit); - { - N(SyntaxKind.StructDeclaration); - { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.StructKeyword); - N(SyntaxKind.IdentifierToken, "R2"); - N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - } - N(SyntaxKind.FieldDeclaration); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.FixedStatement); { - N(SyntaxKind.PrivateKeyword); + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.OpenParenToken); N(SyntaxKind.VariableDeclaration); { N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierToken, "R1"); + N(SyntaxKind.IdentifierToken, "scoped"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "F1"); + M(SyntaxKind.IdentifierToken); } } - N(SyntaxKind.SemicolonToken); - } - N(SyntaxKind.IncompleteMember); - { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.SemicolonToken); } } - N(SyntaxKind.FieldDeclaration); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Fixed_03() + { + string source = +@" +fixed (scoped ref readonly int* a = b); +"; + UsingTree(source, + // (2,15): error CS1001: Identifier expected + // fixed (scoped ref readonly int* a = b); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(2, 15), + // (2,15): error CS1003: Syntax error, ',' expected + // fixed (scoped ref readonly int* a = b); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(2, 15) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.FixedStatement); { - N(SyntaxKind.PrivateKeyword); + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.OpenParenToken); N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.RefType); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } + N(SyntaxKind.IdentifierToken, "scoped"); } - N(SyntaxKind.VariableDeclarator); + M(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "F3"); + M(SyntaxKind.IdentifierToken); } } - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } } - N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void Event_01(LanguageVersion langVersion) + [Fact] + public void Catch_01() { string source = @" -ref struct R2 -{ - scoped event int F3; -} -"; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,12): error CS1519: Invalid token 'event' in class, record, struct, or interface member declaration - // scoped event int F3; - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "event").WithArguments("event").WithLocation(4, 12) +try {} +catch (scoped T a) {} +"; + UsingTree(source, + // (3,17): error CS1026: ) expected + // catch (scoped T a) {} + Diagnostic(ErrorCode.ERR_CloseParenExpected, "a").WithLocation(3, 17), + // (3,17): error CS1514: { expected + // catch (scoped T a) {} + Diagnostic(ErrorCode.ERR_LbraceExpected, "a").WithLocation(3, 17), + // (3,18): error CS1002: ; expected + // catch (scoped T a) {} + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(3, 18), + // (3,18): error CS1513: } expected + // catch (scoped T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(3, 18), + // (3,22): error CS1513: } expected + // catch (scoped T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, 22) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.StructDeclaration); + N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.StructKeyword); - N(SyntaxKind.IdentifierToken, "R2"); - N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); + N(SyntaxKind.TryStatement); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.TryKeyword); + N(SyntaxKind.Block); { - N(SyntaxKind.IdentifierToken, "scoped"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); } - } - N(SyntaxKind.EventFieldDeclaration); - { - N(SyntaxKind.EventKeyword); - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.CatchClause); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.CatchKeyword); + N(SyntaxKind.CatchDeclaration); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "T"); + M(SyntaxKind.CloseParenToken); } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.Block); { - N(SyntaxKind.IdentifierToken, "F3"); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); } } - N(SyntaxKind.SemicolonToken); } - N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void Event_02(LanguageVersion langVersion) + [Fact] + public void Catch_02() { string source = @" -ref struct R2 -{ - event scoped int F3; -} -"; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // event scoped int F3; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "int").WithLocation(4, 18), - // (4,18): error CS1514: { expected - // event scoped int F3; - Diagnostic(ErrorCode.ERR_LbraceExpected, "int").WithLocation(4, 18), - // (4,18): error CS1513: } expected - // event scoped int F3; - Diagnostic(ErrorCode.ERR_RbraceExpected, "int").WithLocation(4, 18) +try {} +catch (scoped ref T a) {} +"; + UsingTree(source, + // (3,15): error CS1026: ) expected + // catch (scoped ref T a) {} + Diagnostic(ErrorCode.ERR_CloseParenExpected, "ref").WithLocation(3, 15), + // (3,15): error CS1514: { expected + // catch (scoped ref T a) {} + Diagnostic(ErrorCode.ERR_LbraceExpected, "ref").WithLocation(3, 15), + // (3,22): error CS1002: ; expected + // catch (scoped ref T a) {} + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(3, 22), + // (3,22): error CS1513: } expected + // catch (scoped ref T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(3, 22), + // (3,26): error CS1513: } expected + // catch (scoped ref T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, 26) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.StructDeclaration); + N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.StructKeyword); - N(SyntaxKind.IdentifierToken, "R2"); - N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.EventDeclaration); + N(SyntaxKind.TryStatement); { - N(SyntaxKind.EventKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.IdentifierToken); - M(SyntaxKind.AccessorList); + N(SyntaxKind.TryKeyword); + N(SyntaxKind.Block); { - M(SyntaxKind.OpenBraceToken); - M(SyntaxKind.CloseBraceToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); } - } - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.CatchClause); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.CatchKeyword); + N(SyntaxKind.CatchDeclaration); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + M(SyntaxKind.CloseParenToken); } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.Block); { - N(SyntaxKind.IdentifierToken, "F3"); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); } } - N(SyntaxKind.SemicolonToken); } - N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } EOF(); } - [Theory] - [InlineData(LanguageVersion.CSharp10)] - [InlineData(LanguageVersion.CSharp11)] - public void Event_03(LanguageVersion langVersion) + [Fact] + public void Catch_03() { string source = @" -ref struct R2 -{ - event scoped ref int F3; -} -"; - UsingTree(source, TestOptions.Regular.WithLanguageVersion(langVersion), - // (4,18): error CS1001: Identifier expected - // event scoped ref int F3; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(4, 18), - // (4,18): error CS1514: { expected - // event scoped ref int F3; - Diagnostic(ErrorCode.ERR_LbraceExpected, "ref").WithLocation(4, 18), - // (4,18): error CS1513: } expected - // event scoped ref int F3; - Diagnostic(ErrorCode.ERR_RbraceExpected, "ref").WithLocation(4, 18) +try {} +catch (scoped ref readonly T a) {} +"; + UsingTree(source, + // (3,15): error CS1026: ) expected + // catch (scoped ref readonly T a) {} + Diagnostic(ErrorCode.ERR_CloseParenExpected, "ref").WithLocation(3, 15), + // (3,15): error CS1514: { expected + // catch (scoped ref readonly T a) {} + Diagnostic(ErrorCode.ERR_LbraceExpected, "ref").WithLocation(3, 15), + // (3,31): error CS1002: ; expected + // catch (scoped ref readonly T a) {} + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(3, 31), + // (3,31): error CS1513: } expected + // catch (scoped ref readonly T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(3, 31), + // (3,35): error CS1513: } expected + // catch (scoped ref readonly T a) {} + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, 35) ); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.StructDeclaration); + N(SyntaxKind.GlobalStatement); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.StructKeyword); - N(SyntaxKind.IdentifierToken, "R2"); - N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.EventDeclaration); + N(SyntaxKind.TryStatement); { - N(SyntaxKind.EventKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "scoped"); - } - M(SyntaxKind.IdentifierToken); - M(SyntaxKind.AccessorList); + N(SyntaxKind.TryKeyword); + N(SyntaxKind.Block); { - M(SyntaxKind.OpenBraceToken); - M(SyntaxKind.CloseBraceToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); } - } - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.CatchClause); { - N(SyntaxKind.RefType); + N(SyntaxKind.CatchKeyword); + N(SyntaxKind.CatchDeclaration); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.PredefinedType); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.IdentifierToken, "scoped"); } + M(SyntaxKind.CloseParenToken); } - N(SyntaxKind.VariableDeclarator); + N(SyntaxKind.Block); { - N(SyntaxKind.IdentifierToken, "F3"); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); } } - N(SyntaxKind.SemicolonToken); } - N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.EndOfFileToken); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs index 2757f2136b62a..87edf31432db6 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -679,5 +679,208 @@ public void RefComplexElementInitializer(LanguageVersion languageVersion) } EOF(); } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_01(LanguageVersion languageVersion) + { + string source = "new { ref x }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,7): error CS1525: Invalid expression term 'ref' + // new { ref x } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 7)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_02(LanguageVersion languageVersion) + { + string source = "new { ref x, y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,7): error CS1525: Invalid expression term 'ref' + // new { ref x, y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 7)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_03(LanguageVersion languageVersion) + { + string source = "new { x, ref y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,10): error CS1525: Invalid expression term 'ref' + // new { x, ref y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref y").WithArguments("ref").WithLocation(1, 10)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_04(LanguageVersion languageVersion) + { + string source = "new { P = ref x, y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,11): error CS1525: Invalid expression term 'ref' + // new { P = ref x, y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(1, 11)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.NameEquals); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "P"); + } + N(SyntaxKind.EqualsToken); + } + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void AnonymousType_05(LanguageVersion languageVersion) + { + string source = "new { x, Q = ref y }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,14): error CS1525: Invalid expression term 'ref' + // new { x, Q = ref y } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref y").WithArguments("ref").WithLocation(1, 14)); + + N(SyntaxKind.AnonymousObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AnonymousObjectMemberDeclarator); + { + N(SyntaxKind.NameEquals); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Q"); + } + N(SyntaxKind.EqualsToken); + } + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTreeTests.cs index 6db23aec9d70e..7c22fd842e7d5 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,18 +113,25 @@ static SyntaxTree WithInitializedDirectives(SyntaxTree tree) } } + [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 #pragma warning disable CS0618 [Fact] 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] @@ -248,7 +256,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 +267,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 +298,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 +308,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 +329,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/CSharp/csc/AnyCpu/csc.csproj b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj index b73e339bf48ec..9565c857b130b 100644 --- a/src/Compilers/CSharp/csc/AnyCpu/csc.csproj +++ b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj @@ -5,6 +5,7 @@ Exe net6.0;net472 false + true diff --git a/src/Compilers/CSharp/csc/arm64/csc-arm64.csproj b/src/Compilers/CSharp/csc/arm64/csc-arm64.csproj index 3eed2406b995f..664a77edc7e7e 100644 --- a/src/Compilers/CSharp/csc/arm64/csc-arm64.csproj +++ b/src/Compilers/CSharp/csc/arm64/csc-arm64.csproj @@ -9,6 +9,7 @@ ARM64 net472 true + true 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/Collections/DebuggerAttributes.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/DebuggerAttributes.cs index 65a49d2e305e8..403f2162ec4ce 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/DebuggerAttributes.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/DebuggerAttributes.cs @@ -63,7 +63,7 @@ internal static DebuggerAttributeInfo ValidateDebuggerTypeProxyProperties(Type t // Create an instance of the proxy type, and make sure we can access all of the instance properties // on the type without exception - object proxyInstance = Activator.CreateInstance(proxyType, obj) ?? throw ExceptionUtilities.Unreachable; + object proxyInstance = Activator.CreateInstance(proxyType, obj) ?? throw ExceptionUtilities.Unreachable(); IEnumerable properties = GetDebuggerVisibleProperties(proxyType); return new DebuggerAttributeInfo(proxyInstance, properties); } diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableListTestBase.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableListTestBase.cs index 64aa6c93d73e6..935d0429b5586 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableListTestBase.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableListTestBase.cs @@ -111,7 +111,7 @@ public void CopyToTest() [Fact] public void ForEachTest() { - ForEachImpl(ImmutableSegmentedList.Empty, n => { throw ExceptionUtilities.Unreachable; }); + ForEachImpl(ImmutableSegmentedList.Empty, n => { throw ExceptionUtilities.Unreachable(); }); var list = ImmutableSegmentedList.Empty.AddRange(Enumerable.Range(5, 3)); var hitTest = new bool[list.Max() + 1]; @@ -159,7 +159,7 @@ public void FindTest() [Fact] public void FindLastTest() { - Assert.Equal(0, FindLastImpl(ImmutableSegmentedList.Empty, n => { throw ExceptionUtilities.Unreachable; })); + Assert.Equal(0, FindLastImpl(ImmutableSegmentedList.Empty, n => { throw ExceptionUtilities.Unreachable(); })); var list = ImmutableSegmentedList.Empty.AddRange(new[] { 2, 3, 4, 5, 6 }); Assert.Equal(5, FindLastImpl(list, n => (n % 2) == 1)); } diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableSegmentedListTest.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableSegmentedListTest.cs index b689b9a42912b..87f16a050f0e5 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableSegmentedListTest.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableSegmentedListTest.cs @@ -413,7 +413,7 @@ public void RemoveNonExistentKeepsReference() public void RemoveRangeDoesNotEnumerateSequenceIfThisIsEmpty() { var list = ImmutableSegmentedList.Empty; - list.RemoveRange(Enumerable.Range(1, 1).Select(n => { throw ExceptionUtilities.Unreachable; })); + list.RemoveRange(Enumerable.Range(1, 1).Select(n => { throw ExceptionUtilities.Unreachable(); })); } [Fact] diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs index e6a06f9ad5d59..a014c55e8adc0 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs @@ -41,7 +41,7 @@ public void CalculateSegmentSize(int elementSize) 10 => 8192, 16 => 4096, 32 => 2048, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.Equal(expected, SegmentedArrayHelper.TestAccessor.CalculateSegmentSize(elementSize)); @@ -66,7 +66,7 @@ public void CalculateSegmentShift(int segmentSize) 16384 => 14, 32768 => 15, 65536 => 16, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.Equal(expected, SegmentedArrayHelper.TestAccessor.CalculateSegmentShift(segmentSize)); @@ -91,7 +91,7 @@ public void CalculateOffsetMask(int segmentSize) 16384 => 0x3FFF, 32768 => 0x7FFF, 65536 => 0xFFFF, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.Equal(expected, SegmentedArrayHelper.TestAccessor.CalculateOffsetMask(segmentSize)); diff --git a/src/Compilers/Core/CodeAnalysisTest/DefaultAnalyzerAssemblyLoaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/DefaultAnalyzerAssemblyLoaderTests.cs index 74ee973a5ceb5..25f19a4388306 100644 --- a/src/Compilers/Core/CodeAnalysisTest/DefaultAnalyzerAssemblyLoaderTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/DefaultAnalyzerAssemblyLoaderTests.cs @@ -153,7 +153,7 @@ public void AssemblyLoading_DependencyLocationNotAdded() private static void VerifyAssemblies(IEnumerable assemblies, params (string simpleName, string version, string path)[] expected) { - Assert.Equal(expected, assemblies.Select(assembly => (assembly.GetName().Name!, assembly.GetName().Version!.ToString(), assembly.Location)).Order()); + Assert.Equal(expected, Roslyn.Utilities.EnumerableExtensions.Order(assemblies.Select(assembly => (assembly.GetName().Name!, assembly.GetName().Version!.ToString(), assembly.Location)))); } [ConditionalFact(typeof(CoreClrOnly))] 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/AssemblyIdentityTests.cs b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityTests.cs index ee0417c80a2b8..bf9632aae91c6 100644 --- a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/AssemblyIdentityTests.cs @@ -89,7 +89,10 @@ public void FromAssemblyDefinition() name.CultureInfo = new CultureInfo("en-US", useUserOverride: false); name.ContentType = AssemblyContentType.Default; name.Version = new Version(1, 2, 3, 4); +#pragma warning disable SYSLIB0037 + // warning SYSLIB0037: 'AssemblyName.ProcessorArchitecture' is obsolete: 'AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete and not supported.' name.ProcessorArchitecture = ProcessorArchitecture.X86; +#pragma warning restore SYSLIB0037 var id = AssemblyIdentity.FromAssemblyDefinition(name); Assert.Equal("goo", id.Name); @@ -128,7 +131,9 @@ public void FromAssemblyDefinition_InvariantCulture() name.CultureInfo = CultureInfo.InvariantCulture; name.ContentType = AssemblyContentType.Default; name.Version = new Version(1, 2, 3, 4); +#pragma warning disable SYSLIB0037 name.ProcessorArchitecture = ProcessorArchitecture.X86; +#pragma warning restore SYSLIB0037 var id = AssemblyIdentity.FromAssemblyDefinition(name); Assert.Equal("", id.CultureName); @@ -311,7 +316,10 @@ public void ToAssemblyName() AssertEx.Equal(new byte[0], an.GetPublicKeyToken()); AssertEx.Equal(null, an.GetPublicKey()); Assert.Equal(AssemblyNameFlags.None, an.Flags); +#pragma warning disable SYSLIB0044 + // warning SYSLIB0044: 'AssemblyName.CodeBase' is obsolete: 'AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported.' Assert.Null(an.CodeBase); +#pragma warning restore SYSLIB0044 ai = new AssemblyIdentity("goo", new Version(1, 2, 3, 4), "en-US", RoPublicKey1, hasPublicKey: true, @@ -324,7 +332,9 @@ public void ToAssemblyName() AssertEx.Equal(PublicKeyToken1, an.GetPublicKeyToken()); AssertEx.Equal(PublicKey1, an.GetPublicKey()); Assert.Equal(AssemblyNameFlags.PublicKey | AssemblyNameFlags.Retargetable, an.Flags); +#pragma warning disable SYSLIB0044 Assert.Null(an.CodeBase); +#pragma warning restore SYSLIB0044 // invalid characters are ok in the name, the FullName can't be built though: foreach (char c in ClrInvalidCharacters) @@ -338,7 +348,9 @@ public void ToAssemblyName() AssertEx.Equal(new byte[0], an.GetPublicKeyToken()); AssertEx.Equal(null, an.GetPublicKey()); Assert.Equal(AssemblyNameFlags.None, an.Flags); +#pragma warning disable SYSLIB0044 AssertEx.Equal(null, an.CodeBase); +#pragma warning restore SYSLIB0044 } } 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/MetadataReferences/ModuleMetadataTests.cs b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/ModuleMetadataTests.cs index 4ec0be5bd174c..870b1efd6de3b 100644 --- a/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/ModuleMetadataTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/MetadataReferences/ModuleMetadataTests.cs @@ -352,7 +352,10 @@ public unsafe void CreateFromUnmanagedMemoryStream_Prefetcha_LeaveOpenTrue(PEStr } } - [Fact] + /// + /// Only test in 64-bit process. throws if the given length is greater than the size of the available address space. + /// + [ConditionalFact(typeof(Bitness64))] public unsafe void CreateFromUnmanagedMemoryStream_LargeIntSize() { var assembly = TestResources.Basic.Members; diff --git a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj index a43e46d8035cb..2efc791e007f5 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.UnitTests true - net6.0;net472 + net7.0;net472 @@ -19,6 +19,8 @@ + + diff --git a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs index fbd545a4f92fc..ee863386965cf 100644 --- a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Text; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -395,33 +396,33 @@ public void TestEnumMember() } [Fact] - public void TestInt32EncodingKinds() + public void TestInt32TypeCodes() { - Assert.Equal(ObjectWriter.EncodingKind.Int32_1, ObjectWriter.EncodingKind.Int32_0 + 1); - Assert.Equal(ObjectWriter.EncodingKind.Int32_2, ObjectWriter.EncodingKind.Int32_0 + 2); - Assert.Equal(ObjectWriter.EncodingKind.Int32_3, ObjectWriter.EncodingKind.Int32_0 + 3); - Assert.Equal(ObjectWriter.EncodingKind.Int32_4, ObjectWriter.EncodingKind.Int32_0 + 4); - Assert.Equal(ObjectWriter.EncodingKind.Int32_5, ObjectWriter.EncodingKind.Int32_0 + 5); - Assert.Equal(ObjectWriter.EncodingKind.Int32_6, ObjectWriter.EncodingKind.Int32_0 + 6); - Assert.Equal(ObjectWriter.EncodingKind.Int32_7, ObjectWriter.EncodingKind.Int32_0 + 7); - Assert.Equal(ObjectWriter.EncodingKind.Int32_8, ObjectWriter.EncodingKind.Int32_0 + 8); - Assert.Equal(ObjectWriter.EncodingKind.Int32_9, ObjectWriter.EncodingKind.Int32_0 + 9); - Assert.Equal(ObjectWriter.EncodingKind.Int32_10, ObjectWriter.EncodingKind.Int32_0 + 10); + Assert.Equal(ObjectWriter.TypeCode.Int32_1, ObjectWriter.TypeCode.Int32_0 + 1); + Assert.Equal(ObjectWriter.TypeCode.Int32_2, ObjectWriter.TypeCode.Int32_0 + 2); + Assert.Equal(ObjectWriter.TypeCode.Int32_3, ObjectWriter.TypeCode.Int32_0 + 3); + Assert.Equal(ObjectWriter.TypeCode.Int32_4, ObjectWriter.TypeCode.Int32_0 + 4); + Assert.Equal(ObjectWriter.TypeCode.Int32_5, ObjectWriter.TypeCode.Int32_0 + 5); + Assert.Equal(ObjectWriter.TypeCode.Int32_6, ObjectWriter.TypeCode.Int32_0 + 6); + Assert.Equal(ObjectWriter.TypeCode.Int32_7, ObjectWriter.TypeCode.Int32_0 + 7); + Assert.Equal(ObjectWriter.TypeCode.Int32_8, ObjectWriter.TypeCode.Int32_0 + 8); + Assert.Equal(ObjectWriter.TypeCode.Int32_9, ObjectWriter.TypeCode.Int32_0 + 9); + Assert.Equal(ObjectWriter.TypeCode.Int32_10, ObjectWriter.TypeCode.Int32_0 + 10); } [Fact] - public void TestUInt32EncodingKinds() + public void TestUInt32TypeCodes() { - Assert.Equal(ObjectWriter.EncodingKind.UInt32_1, ObjectWriter.EncodingKind.UInt32_0 + 1); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_2, ObjectWriter.EncodingKind.UInt32_0 + 2); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_3, ObjectWriter.EncodingKind.UInt32_0 + 3); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_4, ObjectWriter.EncodingKind.UInt32_0 + 4); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_5, ObjectWriter.EncodingKind.UInt32_0 + 5); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_6, ObjectWriter.EncodingKind.UInt32_0 + 6); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_7, ObjectWriter.EncodingKind.UInt32_0 + 7); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_8, ObjectWriter.EncodingKind.UInt32_0 + 8); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_9, ObjectWriter.EncodingKind.UInt32_0 + 9); - Assert.Equal(ObjectWriter.EncodingKind.UInt32_10, ObjectWriter.EncodingKind.UInt32_0 + 10); + Assert.Equal(ObjectWriter.TypeCode.UInt32_1, ObjectWriter.TypeCode.UInt32_0 + 1); + Assert.Equal(ObjectWriter.TypeCode.UInt32_2, ObjectWriter.TypeCode.UInt32_0 + 2); + Assert.Equal(ObjectWriter.TypeCode.UInt32_3, ObjectWriter.TypeCode.UInt32_0 + 3); + Assert.Equal(ObjectWriter.TypeCode.UInt32_4, ObjectWriter.TypeCode.UInt32_0 + 4); + Assert.Equal(ObjectWriter.TypeCode.UInt32_5, ObjectWriter.TypeCode.UInt32_0 + 5); + Assert.Equal(ObjectWriter.TypeCode.UInt32_6, ObjectWriter.TypeCode.UInt32_0 + 6); + Assert.Equal(ObjectWriter.TypeCode.UInt32_7, ObjectWriter.TypeCode.UInt32_0 + 7); + Assert.Equal(ObjectWriter.TypeCode.UInt32_8, ObjectWriter.TypeCode.UInt32_0 + 8); + Assert.Equal(ObjectWriter.TypeCode.UInt32_9, ObjectWriter.TypeCode.UInt32_0 + 9); + Assert.Equal(ObjectWriter.TypeCode.UInt32_10, ObjectWriter.TypeCode.UInt32_0 + 10); } private void TestRoundTripType(Type type) @@ -1138,62 +1139,26 @@ private void TestRoundTripArray(T[] values) TestRoundTripValue(values); } - [Theory] - [CombinatorialData] - public void Encoding_Utf8(bool byteOrderMark) - { - TestRoundtripEncoding(new UTF8Encoding(byteOrderMark)); - } - - [Theory] - [CombinatorialData] - public void Encoding_UTF32(bool bigEndian, bool byteOrderMark) - { - TestRoundtripEncoding(new UTF32Encoding(bigEndian, byteOrderMark)); - } + public static IEnumerable GetEncodingTestCases() + => EncodingTestHelpers.GetEncodingTestCases(); [Theory] - [CombinatorialData] - public void Encoding_Unicode(bool bigEndian, bool byteOrderMark) - { - TestRoundtripEncoding(new UnicodeEncoding(bigEndian, byteOrderMark)); - } - - [Fact] - public void Encoding_AllAvailable() - { - foreach (var info in Encoding.GetEncodings()) - { - TestRoundtripEncoding(Encoding.GetEncoding(info.Name)); - } - } - - private static void TestRoundtripEncoding(Encoding encoding) + [MemberData(nameof(GetEncodingTestCases))] + public void Encodings(Encoding original) { using var stream = new MemoryStream(); using (var writer = new ObjectWriter(stream, leaveOpen: true)) { - writer.WriteEncoding(encoding); + writer.WriteEncoding(original); } stream.Position = 0; using var reader = ObjectReader.TryGetReader(stream); Assert.NotNull(reader); - var actualEncoding = (Encoding)((Encoding)reader.ReadValue()).Clone(); - var expectedEncoding = (Encoding)encoding.Clone(); - - // set the fallbacks to the same instance so that equality comparison does not take them into account: - actualEncoding.EncoderFallback = EncoderFallback.ExceptionFallback; - actualEncoding.DecoderFallback = DecoderFallback.ExceptionFallback; - expectedEncoding.EncoderFallback = EncoderFallback.ExceptionFallback; - expectedEncoding.DecoderFallback = DecoderFallback.ExceptionFallback; - - Assert.Equal(expectedEncoding.GetPreamble(), actualEncoding.GetPreamble()); - Assert.Equal(expectedEncoding.CodePage, actualEncoding.CodePage); - Assert.Equal(expectedEncoding.WebName, actualEncoding.WebName); - Assert.Equal(expectedEncoding, actualEncoding); + var deserialized = (Encoding)reader.ReadValue(); + EncodingTestHelpers.AssertEncodingsEqual(original, deserialized); } [Fact] diff --git a/src/Compilers/Core/CodeAnalysisTest/PDB/CustomDebugInfoReaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/PDB/CustomDebugInfoReaderTests.cs new file mode 100644 index 0000000000000..4194269127de6 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/PDB/CustomDebugInfoReaderTests.cs @@ -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. + +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Debugging; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public class CustomDebugInfoReaderTests +{ + [Theory] + [InlineData(new byte[0], "")] + [InlineData(new byte[] { 0x00, 0x00 }, "")] + [InlineData(new byte[] { (byte)'a', 0x00 }, "a")] + [InlineData(new byte[] { (byte)'a', 0x00, 0x00, 0x00 }, "a")] + public void DecodeForwardIteratorRecord(byte[] bytes, string expected) + { + Assert.Equal(expected, CustomDebugInfoReader.DecodeForwardIteratorRecord(bytes.ToImmutableArray())); + } + + [Theory] + [InlineData(new byte[] { 0x00 })] + [InlineData(new byte[] { (byte)'a', })] + [InlineData(new byte[] { (byte)'a', 0x00, 0x00 })] + public void DecodeForwardIteratorRecord_Invalid(byte[] bytes) + { + Assert.Throws(() => CustomDebugInfoReader.DecodeForwardIteratorRecord(bytes.ToImmutableArray())); + } +} 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/MSBuildTaskTests/DotNetSdkTests.cs b/src/Compilers/Core/MSBuildTaskTests/DotNetSdkTests.cs index 13038c587f1aa..0aa0d9e8f7f98 100644 --- a/src/Compilers/Core/MSBuildTaskTests/DotNetSdkTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/DotNetSdkTests.cs @@ -7,11 +7,17 @@ using System.IO; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests { public class DotNetSdkTests : DotNetSdkTestBase { + public DotNetSdkTests(ITestOutputHelper testOutputHelper) + : base(testOutputHelper) + { + } + [ConditionalFact(typeof(DotNetSdkAvailable), AlwaysSkip = "https://github.com/dotnet/roslyn/issues/46304")] [WorkItem(22835, "https://github.com/dotnet/roslyn/issues/22835")] public void TestSourceLink() diff --git a/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj b/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj index 1d515f35f8168..490911cf37c6e 100644 --- a/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj +++ b/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.BuildTasks.UnitTests true - net6.0;net472 + net7.0;net472 true diff --git a/src/Compilers/Core/MSBuildTaskTests/TestUtilities/DotNetSdkTestBase.cs b/src/Compilers/Core/MSBuildTaskTests/TestUtilities/DotNetSdkTestBase.cs index 5bea2edc39f20..774a44e3c1ded 100644 --- a/src/Compilers/Core/MSBuildTaskTests/TestUtilities/DotNetSdkTestBase.cs +++ b/src/Compilers/Core/MSBuildTaskTests/TestUtilities/DotNetSdkTestBase.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests { @@ -46,6 +47,7 @@ public void F() } "; + protected readonly ITestOutputHelper TestOutputHelper; protected readonly TempDirectory ProjectDir; protected readonly TempDirectory ObjDir; protected readonly TempDirectory OutDir; @@ -89,14 +91,17 @@ static bool isMatchingDotNetInstance(string? dotnetDir) private static void EmitTestHelperProps( string objDirectory, string projectFileName, - string? content) + string? content, + ArtifactUploadUtil? uploadUtil) { // Common.props automatically import {project-name}.*.props files from MSBuildProjectExtensionsPath directory, // which is by default set to the IntermediateOutputPath: - File.WriteAllText(Path.Combine(objDirectory, projectFileName + ".TestHelpers.g.props"), + var filePath = Path.Combine(objDirectory, projectFileName + ".TestHelpers.g.props"); + File.WriteAllText(filePath, $@" {content} "); + uploadUtil?.AddArtifact(filePath); } private static void EmitTestHelperTargets( @@ -104,11 +109,13 @@ private static void EmitTestHelperTargets( string outputFile, string projectFileName, IEnumerable expressions, - string? additionalContent) + string? additionalContent, + ArtifactUploadUtil? uploadUtil) { // Common.targets automatically import {project-name}.*.targets files from MSBuildProjectExtensionsPath directory, // which is by defautl set to the IntermediateOutputPath: - File.WriteAllText(Path.Combine(objDirectory, projectFileName + ".TestHelpers.g.targets"), + var filePath = Path.Combine(objDirectory, projectFileName + ".TestHelpers.g.targets"); + File.WriteAllText(filePath, $@" @@ -133,9 +140,11 @@ private static void EmitTestHelperTargets( {additionalContent} "); + + uploadUtil?.AddArtifact(filePath); } - public DotNetSdkTestBase() + public DotNetSdkTestBase(ITestOutputHelper testOutputHelper) { Assert.True(s_dotnetInstallDir is object, $"SDK not found. Use {nameof(ConditionalFactAttribute)}(typeof({nameof(DotNetSdkAvailable)})) to skip the test if the SDK is not found."); Debug.Assert(s_dotnetInstallDir is object); @@ -144,6 +153,7 @@ public DotNetSdkTestBase() var testBinDirectory = Path.GetDirectoryName(typeof(DotNetSdkTests).Assembly.Location) ?? string.Empty; var sdksDir = Path.Combine(s_dotnetSdkPath ?? string.Empty, "Sdks"); + TestOutputHelper = testOutputHelper; ProjectName = "test"; ProjectFileName = ProjectName + ".csproj"; Configuration = "Debug"; @@ -186,14 +196,16 @@ public DotNetSdkTestBase() protected void VerifyValues(string? customProps, string? customTargets, string[] targets, string[] expressions, string[] expectedResults) { + using var uploadUtil = new ArtifactUploadUtil(TestOutputHelper); var evaluationResultsFile = Path.Combine(OutDir.Path, "EvaluationResult.txt"); - EmitTestHelperProps(ObjDir.Path, ProjectFileName, customProps); - EmitTestHelperTargets(ObjDir.Path, evaluationResultsFile, ProjectFileName, expressions, customTargets); + EmitTestHelperProps(ObjDir.Path, ProjectFileName, customProps, uploadUtil); + EmitTestHelperTargets(ObjDir.Path, evaluationResultsFile, ProjectFileName, expressions, customTargets, uploadUtil); var targetsArg = string.Join(";", targets.Concat(new[] { "Test_EvaluateExpressions" })); var testBinDirectory = Path.GetDirectoryName(typeof(DotNetSdkTests).Assembly.Location); var binLog = Path.Combine(ProjectDir.Path, $"build{_logIndex++}.binlog"); + uploadUtil.AddArtifact(binLog); // RoslynTargetsPath is a path to the built-in Roslyn compilers in the .NET SDK. // For testing we are using compilers from custom location (this emulates usage of Microsoft.Net.Compilers package. @@ -204,6 +216,7 @@ protected void VerifyValues(string? customProps, string? customTargets, string[] var evaluationResult = File.ReadAllLines(evaluationResultsFile).Select(l => (l != EmptyValueMarker) ? l : ""); AssertEx.Equal(expectedResults, evaluationResult); + uploadUtil.SetSucceeded(); } } } diff --git a/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs b/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs index ad334c416e8cb..c52fa554499d9 100644 --- a/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs +++ b/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs @@ -369,13 +369,13 @@ public override string ToString() public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/CodeGen/LocalDefinition.cs b/src/Compilers/Core/Portable/CodeGen/LocalDefinition.cs index e002308b2c220..b17ebbf972bda 100644 --- a/src/Compilers/Core/Portable/CodeGen/LocalDefinition.cs +++ b/src/Compilers/Core/Portable/CodeGen/LocalDefinition.cs @@ -106,7 +106,7 @@ public Location Location public MetadataConstant CompileTimeValue { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public ImmutableArray CustomModifiers @@ -114,7 +114,7 @@ public ImmutableArray CustomModifiers public bool IsConstant { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public bool IsModified => false; diff --git a/src/Compilers/Core/Portable/CodeGen/LocalScopeManager.cs b/src/Compilers/Core/Portable/CodeGen/LocalScopeManager.cs index c70e6f12c57bb..fdf0cd3b88c5a 100644 --- a/src/Compilers/Core/Portable/CodeGen/LocalScopeManager.cs +++ b/src/Compilers/Core/Portable/CodeGen/LocalScopeManager.cs @@ -226,7 +226,7 @@ public virtual void CloseScope(ILBuilder builder) public virtual void FinishFilterCondition(ILBuilder builder) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public bool IsExceptionHandler diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index 6d97be32cc7ab..813e84129b4c3 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -402,7 +402,7 @@ internal SynthesizedStaticField(string name, Cci.INamedTypeDefinition containing public int Offset { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public Cci.ITypeDefinition ContainingTypeDefinition => _containingType; @@ -421,7 +421,7 @@ public void Dispatch(Cci.MetadataVisitor visitor) public Cci.IDefinition AsDefinition(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Symbols.ISymbolInternal? Cci.IReference.GetInternalSymbol() => null; @@ -444,19 +444,19 @@ public Cci.IDefinition AsDefinition(EmitContext context) public MetadataConstant Constant { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } @@ -573,7 +573,7 @@ public IEnumerable SecurityAttributes public TypeDefinitionHandle TypeDef { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public Cci.IGenericMethodParameterReference? AsGenericMethodParameterReference => null; @@ -602,7 +602,7 @@ public TypeDefinitionHandle TypeDef public virtual Cci.ITypeReference GetBaseClass(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public virtual LayoutKind Layout => LayoutKind.Auto; @@ -611,7 +611,7 @@ public virtual Cci.ITypeReference GetBaseClass(EmitContext context) public virtual void Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public virtual bool IsValueType => false; @@ -619,13 +619,13 @@ public virtual void Dispatch(Cci.MetadataVisitor visitor) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/CodeGen/SignatureOnlyLocalDefinition.cs b/src/Compilers/Core/Portable/CodeGen/SignatureOnlyLocalDefinition.cs index 7bbea7ed78971..705ed6d903356 100644 --- a/src/Compilers/Core/Portable/CodeGen/SignatureOnlyLocalDefinition.cs +++ b/src/Compilers/Core/Portable/CodeGen/SignatureOnlyLocalDefinition.cs @@ -28,12 +28,12 @@ internal SignatureOnlyLocalDefinition(byte[] signature, int slot) public MetadataConstant CompileTimeValue { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public ImmutableArray CustomModifiers { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public ImmutableArray DynamicTransformFlags @@ -54,17 +54,17 @@ public ImmutableArray TupleElementNames public bool IsPinned { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public bool IsReference { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public LocalSlotConstraints Constraints { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public Location Location => Location.None; @@ -75,7 +75,7 @@ public LocalSlotConstraints Constraints public Cci.ITypeReference Type { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public byte[] Signature => _signature; diff --git a/src/Compilers/Core/Portable/Collections/TemporaryArray`1.cs b/src/Compilers/Core/Portable/Collections/TemporaryArray`1.cs index ae03ad8c7f184..ec7e4df56013c 100644 --- a/src/Compilers/Core/Portable/Collections/TemporaryArray`1.cs +++ b/src/Compilers/Core/Portable/Collections/TemporaryArray`1.cs @@ -239,7 +239,7 @@ public ImmutableArray ToImmutableAndClear() 2 => ImmutableArray.Create(_item0, _item1), 3 => ImmutableArray.Create(_item0, _item1, _item2), 4 => ImmutableArray.Create(_item0, _item1, _item2, _item3), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; // Since _builder is null on this path, we can overwrite the whole structure to Empty to reset all diff --git a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs index 9da01bd4e94e2..ba608924cd3a3 100644 --- a/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs +++ b/src/Compilers/Core/Portable/Compilation.EmitStreamProvider.cs @@ -61,7 +61,7 @@ internal SimpleEmitStreamProvider(Stream stream) protected override Stream CreateStream(DiagnosticBag diagnostics) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs index 1b7160d8f17cf..45ecd74cdac89 100644 --- a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs +++ b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs @@ -270,10 +270,10 @@ protected abstract SemanticModel? ParentModelCore } /// - /// If this is a non-speculative member semantic model, then returns the containing semantic model for the entire tree. + /// If this is an instance of semantic model that cannot be exposed to external consumers, then returns the containing public semantic model. /// Otherwise, returns this instance of the semantic model. /// - internal abstract SemanticModel ContainingModelOrSelf + internal abstract SemanticModel ContainingPublicModelOrSelf { get; } 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/Diagnostic/DiagnosticInfo.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs index a1394dc9feb3f..b0f60306a27aa 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs @@ -493,7 +493,7 @@ public sealed override bool Equals(object? obj) internal virtual DiagnosticInfo GetResolvedInfo() { // We should never call GetResolvedInfo on a non-lazy DiagnosticInfo - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Diagnostic/ExternalFileLocation.cs b/src/Compilers/Core/Portable/Diagnostic/ExternalFileLocation.cs index a1a04a7199326..6fcb6d2d50e71 100644 --- a/src/Compilers/Core/Portable/Diagnostic/ExternalFileLocation.cs +++ b/src/Compilers/Core/Portable/Diagnostic/ExternalFileLocation.cs @@ -14,12 +14,20 @@ namespace Microsoft.CodeAnalysis internal sealed class ExternalFileLocation : Location, IEquatable { private readonly TextSpan _sourceSpan; - private readonly FileLinePositionSpan _lineSpan; + private readonly FileLinePositionSpan _lineSpan, _mappedLineSpan; internal ExternalFileLocation(string filePath, TextSpan sourceSpan, LinePositionSpan lineSpan) { _sourceSpan = sourceSpan; _lineSpan = new FileLinePositionSpan(filePath, lineSpan); + _mappedLineSpan = _lineSpan; + } + + internal ExternalFileLocation(string filePath, TextSpan sourceSpan, LinePositionSpan lineSpan, string mappedFilePath, LinePositionSpan mappedLineSpan) + { + _sourceSpan = sourceSpan; + _lineSpan = new FileLinePositionSpan(filePath, lineSpan); + _mappedLineSpan = new FileLinePositionSpan(mappedFilePath, mappedLineSpan, hasMappedPath: true); } public override TextSpan SourceSpan @@ -30,8 +38,6 @@ public override TextSpan SourceSpan } } - public string FilePath => _lineSpan.Path; - public override FileLinePositionSpan GetLineSpan() { return _lineSpan; @@ -39,7 +45,7 @@ public override FileLinePositionSpan GetLineSpan() public override FileLinePositionSpan GetMappedLineSpan() { - return _lineSpan; + return _mappedLineSpan; } public override LocationKind Kind @@ -64,12 +70,14 @@ public bool Equals(ExternalFileLocation? obj) return obj != null && _sourceSpan == obj._sourceSpan - && _lineSpan.Equals(obj._lineSpan); + && _lineSpan.Equals(obj._lineSpan) + && _mappedLineSpan.Equals(obj._mappedLineSpan); } public override int GetHashCode() { - return Hash.Combine(_lineSpan.GetHashCode(), _sourceSpan.GetHashCode()); + return Hash.Combine(_lineSpan.GetHashCode(), + Hash.Combine(_mappedLineSpan.GetHashCode(), _sourceSpan.GetHashCode())); } } } diff --git a/src/Compilers/Core/Portable/Diagnostic/Location.cs b/src/Compilers/Core/Portable/Diagnostic/Location.cs index 15a1f86e4a10b..11fc6cb6bf0f8 100644 --- a/src/Compilers/Core/Portable/Diagnostic/Location.cs +++ b/src/Compilers/Core/Portable/Diagnostic/Location.cs @@ -177,5 +177,23 @@ public static Location Create(string filePath, TextSpan textSpan, LinePositionSp return new ExternalFileLocation(filePath, textSpan, lineSpan); } + + /// + /// Creates an instance of a for a span in a file with a mapped file and span. + /// + public static Location Create(string filePath, TextSpan textSpan, LinePositionSpan lineSpan, string mappedFilePath, LinePositionSpan mappedLineSpan) + { + if (filePath == null) + { + throw new ArgumentNullException(nameof(filePath)); + } + + if (mappedFilePath == null) + { + throw new ArgumentNullException(nameof(mappedFilePath)); + } + + return new ExternalFileLocation(filePath, textSpan, lineSpan, mappedFilePath, mappedLineSpan); + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisResultBuilder.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisResultBuilder.cs index 3d83a888ecb37..a2ec4a7a7a531 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisResultBuilder.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisResultBuilder.cs @@ -171,7 +171,7 @@ internal void ApplySuppressionsAndStoreAnalysisResult(AnalysisScope analysisScop // Fetch the first additional file that matches diagnostic location. if (diagnostic.Location is ExternalFileLocation externalFileLocation) { - if (_pathToAdditionalTextMap.TryGetValue(externalFileLocation.FilePath, out var additionalTexts)) + if (_pathToAdditionalTextMap.TryGetValue(externalFileLocation.GetLineSpan().Path, out var additionalTexts)) { foreach (var additionalText in additionalTexts) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs index 44a4a0b4e8ed4..f1fcdb174b827 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs @@ -198,7 +198,7 @@ public bool ShouldInclude(Diagnostic diagnostic) else if (diagnostic.Location is ExternalFileLocation externalFileLocation) { if (FilterFileOpt.Value.AdditionalFile == null || - !PathUtilities.Comparer.Equals(externalFileLocation.FilePath, FilterFileOpt.Value.AdditionalFile.Path)) + !PathUtilities.Comparer.Equals(externalFileLocation.GetLineSpan().Path, FilterFileOpt.Value.AdditionalFile.Path)) { return false; } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs index 112cb7f154549..82e963b0383a5 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs @@ -125,7 +125,7 @@ public async Task OnCompilationEventsGeneratedAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index fb9f2ed41f367..0a18654af85ad 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -1431,7 +1431,7 @@ private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, An } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1490,7 +1490,7 @@ private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, An } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs index 0339e1d452ab2..35e0cd8c1e709 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs @@ -124,7 +124,7 @@ bool isLocalDiagnostic(Diagnostic diagnostic) if (_contextFile?.AdditionalFile != null && diagnostic.Location is ExternalFileLocation externalFileLocation) { - return PathUtilities.Comparer.Equals(_contextFile.Value.AdditionalFile.Path, externalFileLocation.FilePath); + return PathUtilities.Comparer.Equals(_contextFile.Value.AdditionalFile.Path, externalFileLocation.GetLineSpan().Path); } return false; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 855a5202fc6b8..87ca275bccd7e 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -598,7 +598,7 @@ private async Task ComputeAnalyzerSyntaxDiagnosticsAsync(AnalysisScope analysisS } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -703,7 +703,7 @@ private async Task ComputeAnalyzerSemanticDiagnosticsAsync(SemanticModel model, } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } async Task processPartialSymbolLocationsAsync(ImmutableArray compilationEvents, AnalysisScope analysisScope) @@ -834,7 +834,7 @@ await Task.WhenAll(partialTrees.Select(tree => } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }, linkedCancellationToken), cancellationSource); @@ -873,7 +873,7 @@ await Task.WhenAll(partialTrees.Select(tree => } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -993,7 +993,7 @@ private async Task GetAnalyzerDriverAsync(CancellationToken canc } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1040,7 +1040,7 @@ private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, As } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1170,7 +1170,7 @@ private async Task SetActiveTreeAnalysisTaskAsync(Func GetAnalyzerTelemetryInfoAsync(Diagnosti } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/DocumentationComments/XmlDocumentationCommentTextReader.XmlStream.cs b/src/Compilers/Core/Portable/DocumentationComments/XmlDocumentationCommentTextReader.XmlStream.cs index a4f254a0339d3..7f29a99465f4c 100644 --- a/src/Compilers/Core/Portable/DocumentationComments/XmlDocumentationCommentTextReader.XmlStream.cs +++ b/src/Compilers/Core/Portable/DocumentationComments/XmlDocumentationCommentTextReader.XmlStream.cs @@ -144,13 +144,13 @@ private static int EncodeAndAdvance(string src, int srcIndex, char[] dest, ref i public override int Read() { // XmlReader does not call this API - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int Peek() { // XmlReader does not call this API - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/EmbeddedText.cs b/src/Compilers/Core/Portable/EmbeddedText.cs index 2c36ce81496aa..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; @@ -376,7 +375,7 @@ public override void WriteByte(byte value) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeletedMethodDefinition.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeletedMethodDefinition.cs index abba1af524ace..7fe533be9349d 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeletedMethodDefinition.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeletedMethodDefinition.cs @@ -161,13 +161,13 @@ public ITypeReference GetType(EmitContext context) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/ErrorType.cs b/src/Compilers/Core/Portable/Emit/ErrorType.cs index 83d61eb70c3c3..f7d2ef047a65e 100644 --- a/src/Compilers/Core/Portable/Emit/ErrorType.cs +++ b/src/Compilers/Core/Portable/Emit/ErrorType.cs @@ -193,13 +193,13 @@ string Cci.INamedEntity.Name public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs index fc8127092658e..97ad554541246 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs @@ -106,12 +106,12 @@ private ImmutableArray GetAttributes(TPEModuleBuilder moduleBuil void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => null; @@ -119,13 +119,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs index 6794f9e95faf8..5dfd96e1ed452 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs @@ -210,7 +210,7 @@ ImmutableArray Cci.IParameterDefinition.MarshallingDescriptor void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) @@ -273,13 +273,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs index 2ab12dba66c50..dadc6b42067aa 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs @@ -549,7 +549,7 @@ System.Runtime.InteropServices.CharSet Cci.ITypeDefinition.StringFormat void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) @@ -717,13 +717,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs index 799005de1ab02..351c2c2fc8dd7 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs @@ -196,7 +196,7 @@ Cci.ITypeDefinition Cci.ITypeReference.AsTypeDefinition(EmitContext context) void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) @@ -227,13 +227,13 @@ Cci.IMethodReference Cci.IGenericMethodParameterReference.DefiningMethod public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs b/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs index 87d5f20a81d57..0d547627efdb1 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs @@ -260,13 +260,13 @@ Cci.ITypeReference Cci.ISignature.GetType(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index bd94b713a2441..5a68010063f9e 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -277,5 +277,7 @@ public enum OperationKind ImplicitIndexerReference = 0x7b, /// Indicates an . Utf8String = 0x7c, + /// Indicates an . + Attribute = 0x7d, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 22b7f3c81585f..249b1af87b2e4 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3597,6 +3597,29 @@ public interface IUtf8StringOperation : IOperation /// string Value { get; } } + /// + /// Represents the application of an attribute. + /// + /// Current usage: + /// (1) C# attribute application. + /// (2) VB attribute application. + /// + /// + /// + /// This node is associated with the following operation kinds: + /// + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface IAttributeOperation : IOperation + { + /// + /// The operation representing the attribute. This can be a in non-error cases, or an in error cases. + /// + IOperation Operation { get; } + } #endregion #region Implementations @@ -10156,6 +10179,57 @@ internal Utf8StringOperation(string value, SemanticModel? semanticModel, SyntaxN public override void Accept(OperationVisitor visitor) => visitor.VisitUtf8String(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitUtf8String(this, argument); } + internal sealed partial class AttributeOperation : Operation, IAttributeOperation + { + internal AttributeOperation(IOperation operation, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit) + : base(semanticModel, syntax, isImplicit) + { + Operation = SetParentOperation(operation, this); + } + public IOperation Operation { get; } + internal override int ChildOperationsCount => + (Operation is null ? 0 : 1); + internal override IOperation GetCurrent(int slot, int index) + => slot switch + { + 0 when Operation != null + => Operation, + _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), + }; + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) + { + switch (previousSlot) + { + case -1: + if (Operation != null) return (true, 0, 0); + else goto case 0; + case 0: + case 1: + return (false, 1, 0); + default: + throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); + } + } + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) + { + switch (previousSlot) + { + case int.MaxValue: + if (Operation != null) return (true, 0, 0); + else goto case 0; + case 0: + case -1: + return (false, -1, 0); + default: + throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); + } + } + public override ITypeSymbol? Type => null; + internal override ConstantValue? OperationConstantValue => null; + public override OperationKind Kind => OperationKind.Attribute; + public override void Accept(OperationVisitor visitor) => visitor.VisitAttribute(this); + public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitAttribute(this, argument); + } #endregion #region Cloner internal sealed partial class OperationCloner : OperationVisitor @@ -10166,7 +10240,7 @@ internal sealed partial class OperationCloner : OperationVisitor(T? node) where T : IOperation? => (T?)Visit(node, argument: null); - public override IOperation DefaultVisit(IOperation operation, object? argument) => throw ExceptionUtilities.Unreachable; + public override IOperation DefaultVisit(IOperation operation, object? argument) => throw ExceptionUtilities.Unreachable(); private ImmutableArray VisitArray(ImmutableArray nodes) where T : IOperation => nodes.SelectAsArray((n, @this) => @this.Visit(n), this)!; private ImmutableArray<(ISymbol, T)> VisitArray(ImmutableArray<(ISymbol, T)> nodes) where T : IOperation => nodes.SelectAsArray((n, @this) => (n.Item1, @this.Visit(n.Item2)), this)!; public override IOperation VisitBlock(IBlockOperation operation, object? argument) @@ -10759,6 +10833,11 @@ public override IOperation VisitUtf8String(IUtf8StringOperation operation, objec var internalOperation = (Utf8StringOperation)operation; return new Utf8StringOperation(internalOperation.Value, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } + public override IOperation VisitAttribute(IAttributeOperation operation, object? argument) + { + var internalOperation = (AttributeOperation)operation; + return new AttributeOperation(Visit(internalOperation.Operation), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit); + } } #endregion @@ -10897,6 +10976,7 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitSlicePattern(ISlicePatternOperation operation) => DefaultVisit(operation); public virtual void VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation) => DefaultVisit(operation); public virtual void VisitUtf8String(IUtf8StringOperation operation) => DefaultVisit(operation); + public virtual void VisitAttribute(IAttributeOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11032,6 +11112,7 @@ public abstract partial class OperationVisitor public virtual TResult? VisitSlicePattern(ISlicePatternOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitUtf8String(IUtf8StringOperation operation, TArgument argument) => DefaultVisit(operation, argument); + public virtual TResult? VisitAttribute(IAttributeOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion } diff --git a/src/Compilers/Core/Portable/InternalUtilities/CompilerFeatureRequiredAttribute.cs b/src/Compilers/Core/Portable/InternalUtilities/CompilerFeatureRequiredAttribute.cs new file mode 100644 index 0000000000000..8f39ab70e9198 --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/CompilerFeatureRequiredAttribute.cs @@ -0,0 +1,41 @@ +// 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. + +// Copied from: +// https://github.com/dotnet/runtime/blob/fdd104ec5e1d0d2aa24a6723995a98d0124f724b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompilerFeatureRequiredAttribute.cs + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates that compiler support for a particular feature is required for the location where this attribute is applied. + /// + [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] + internal sealed class CompilerFeatureRequiredAttribute : Attribute + { + public CompilerFeatureRequiredAttribute(string featureName) + { + FeatureName = featureName; + } + + /// + /// The name of the compiler feature. + /// + public string FeatureName { get; } + + /// + /// If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand . + /// + public bool IsOptional { get; init; } + + /// + /// The used for the ref structs C# feature. + /// + public const string RefStructs = nameof(RefStructs); + + /// + /// The used for the required members C# feature. + /// + public const string RequiredMembers = nameof(RequiredMembers); + } +} diff --git a/src/Compilers/Core/Portable/InternalUtilities/EncodingExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/EncodingExtensions.cs index ed721c5816e1d..799070e325d4a 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/EncodingExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/EncodingExtensions.cs @@ -8,9 +8,9 @@ using System.IO; using System.Text; -namespace Roslyn.Utilities +namespace Microsoft.CodeAnalysis { - internal static class EncodingExtensions + internal static partial class EncodingExtensions { /// /// Get maximum char count needed to decode the entire stream. 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/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs b/src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs index ba9629189ff06..e2c787f7f2eb4 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; namespace Roslyn.Utilities @@ -24,10 +25,8 @@ internal static Exception UnexpectedValue(object? o) return new InvalidOperationException(output); } - internal static Exception Unreachable - { - get { return new InvalidOperationException("This program location is thought to be unreachable."); } - } + internal static Exception Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0) + => new InvalidOperationException($"This program location is thought to be unreachable. File='{path}' Line={line}"); /// /// Determine if an exception was an , and that the provided token caused the cancellation. diff --git a/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs b/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs index 5947d753111a6..f314408cdc598 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs @@ -40,7 +40,7 @@ internal static void OnFatalException(Exception exception) DumpStackTrace(exception: exception); Environment.FailFast(exception.ToString(), exception); - throw ExceptionUtilities.Unreachable; // to satisfy [DoesNotReturn] + throw ExceptionUtilities.Unreachable(); // to satisfy [DoesNotReturn] } [DebuggerHidden] @@ -50,7 +50,7 @@ internal static void Fail(string message) { DumpStackTrace(message: message); Environment.FailFast(message); - throw ExceptionUtilities.Unreachable; // to satisfy [DoesNotReturn] + throw ExceptionUtilities.Unreachable(); // to satisfy [DoesNotReturn] } /// diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index 328e663df7978..8ed1dbfbfca97 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using Roslyn.Utilities; @@ -224,6 +225,10 @@ public static bool ReportAndCatchUnlessCanceled(Exception exception, Cancellatio private static readonly object s_reportedMarker = new(); + // Do not allow this method to be inlined. That way when we have a dump we can see this frame in the stack and + // can examine things like s_reportedExceptionMessage. Without this, it's a lot tricker as FatalError is linked + // into many assemblies and finding the right type can be much harder. + [MethodImpl(MethodImplOptions.NoInlining)] private static void Report(Exception exception, ErrorSeverity severity = ErrorSeverity.Uncategorized, bool forceDump = false) { // hold onto last exception to make investigation easier diff --git a/src/Compilers/Core/Portable/InternalUtilities/RequiredMemberAttribute.cs b/src/Compilers/Core/Portable/InternalUtilities/RequiredMemberAttribute.cs new file mode 100644 index 0000000000000..278ef8e5850fc --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/RequiredMemberAttribute.cs @@ -0,0 +1,15 @@ +// 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. + +// Copied from: +// https://github.com/dotnet/runtime/blob/fdd104ec5e1d0d2aa24a6723995a98d0124f724b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RequiredMemberAttribute.cs + +namespace System.Runtime.CompilerServices +{ + /// Specifies that a type has required members or that a member is required. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + internal sealed class RequiredMemberAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs index 01cd73e49d1a5..58ea9a75a47f7 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs @@ -31,14 +31,14 @@ void errorHandlingBody(int i) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (OperationCanceledException e) when (cancellationToken.IsCancellationRequested && e.CancellationToken != cancellationToken) { // Parallel.For checks for a specific cancellation token, so make sure we throw with the // correct one. cancellationToken.ThrowIfCancellationRequested(); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/InternalUtilities/SetsRequiredMembersAttribute.cs b/src/Compilers/Core/Portable/InternalUtilities/SetsRequiredMembersAttribute.cs new file mode 100644 index 0000000000000..b1ed8e229118d --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/SetsRequiredMembersAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +// Copied from: +// https://github.com/dotnet/runtime/blob/fdd104ec5e1d0d2aa24a6723995a98d0124f724b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/SetsRequiredMembersAttribute.cs + +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Specifies that this constructor sets all required members for the current type, and callers + /// do not need to set any required members themselves. + /// + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + internal sealed class SetsRequiredMembersAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/MetadataReference/MetadataImageReference.cs b/src/Compilers/Core/Portable/MetadataReference/MetadataImageReference.cs index 56ff70b9e1e9f..bffbe9af554d9 100644 --- a/src/Compilers/Core/Portable/MetadataReference/MetadataImageReference.cs +++ b/src/Compilers/Core/Portable/MetadataReference/MetadataImageReference.cs @@ -33,7 +33,7 @@ protected override Metadata GetMetadataImpl() protected override DocumentationProvider CreateDocumentationProvider() { // documentation provider is initialized in the constructor - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties) 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/NativePdbWriter/PdbWriter.cs b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs index c02ebf0427c89..5ab20388d4918 100644 --- a/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs +++ b/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs @@ -440,7 +440,7 @@ private string GetAssemblyReferenceAlias(IAssemblyReference assembly, HashSet error in compiler - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private void DefineLocalScopes(ImmutableArray scopes, StandaloneSignatureHandle localSignatureHandleOpt) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs index 87468b40d3183..6b45c799305e7 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraph.cs @@ -137,6 +137,15 @@ public static ControlFlowGraph Create(Operations.IParameterInitializerOperation return CreateCore(initializer, nameof(initializer), cancellationToken); } + /// + /// Creates a for the given executable code block . + /// + /// Root attribute operation, which must have a null parent. + /// Optional cancellation token. + public static ControlFlowGraph Create(Operations.IAttributeOperation attribute, CancellationToken cancellationToken = default) + { + return CreateCore(attribute, nameof(attribute), cancellationToken); + } /// /// Creates a for the given executable code block . diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index ce6ca83cac3d3..ec24e49f56ad6 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -98,7 +98,8 @@ public static ControlFlowGraph Create(IOperation body, ControlFlowGraph? parent body.Kind == OperationKind.ConstructorBody || body.Kind == OperationKind.FieldInitializer || body.Kind == OperationKind.PropertyInitializer || - body.Kind == OperationKind.ParameterInitializer, + body.Kind == OperationKind.ParameterInitializer || + body.Kind == OperationKind.Attribute, $"Unexpected root operation kind: {body.Kind}"); Debug.Assert(parent == null); } @@ -444,7 +445,7 @@ void dispatchException([DisallowNull] ControlFlowRegion? fromRegion) continue; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } fromRegion = enclosing; @@ -1308,7 +1309,7 @@ private void AppendNewBlock(BasicBlockBuilder block, bool linkToPrevious = true) if (block.Ordinal != -1) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } block.Ordinal = _blocks.Count; @@ -3803,7 +3804,7 @@ private void AddExceptionStore(ITypeSymbol exceptionType, IOperation? exceptionD public override IOperation VisitCatchClause(ICatchClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation? VisitReturn(IReturnOperation operation, int? captureIdForResult) @@ -5133,7 +5134,7 @@ void checkLoopCondition() return; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Produce "(operand Xor (step >> 31))" @@ -5571,32 +5572,32 @@ private IOperation MakeNullable(IOperation operand, ITypeSymbol type) public override IOperation VisitSwitchCase(ISwitchCaseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitSingleValueCaseClause(ISingleValueCaseClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitDefaultCaseClause(IDefaultCaseClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitRelationalCaseClause(IRelationalCaseClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitRangeCaseClause(IRangeCaseClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation? VisitEnd(IEndOperation operation, int? captureIdForResult) @@ -5791,39 +5792,39 @@ private void HandleVariableDeclarator(IVariableDeclarationOperation declaration, public override IOperation VisitVariableDeclaration(IVariableDeclarationOperation operation, int? captureIdForResult) { // All variable declarators should be handled by VisitVariableDeclarationGroup. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitVariableDeclarator(IVariableDeclaratorOperation operation, int? captureIdForResult) { // All variable declarators should be handled by VisitVariableDeclaration. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitVariableInitializer(IVariableInitializerOperation operation, int? captureIdForResult) { // All variable initializers should be removed from the tree by VisitVariableDeclaration. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitFlowCapture(IFlowCaptureOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitIsNull(IIsNullOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitCaughtException(ICaughtExceptionOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitInvocation(IInvocationOperation operation, int? captureIdForResult) @@ -6264,7 +6265,7 @@ public override IOperation VisitAnonymousFunction(IAnonymousFunctionOperation op public override IOperation VisitFlowAnonymousFunction(IFlowAnonymousFunctionOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitArrayCreation(IArrayCreationOperation operation, int? captureIdForResult) @@ -6714,12 +6715,12 @@ static void pushLeftNodes(IInterpolatedStringAdditionOperation addition, ArrayBu public override IOperation? VisitInterpolatedStringAddition(IInterpolatedStringAdditionOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation? VisitInterpolatedStringAppend(IInterpolatedStringAppendOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation? VisitInterpolatedStringHandlerArgumentPlaceholder(IInterpolatedStringHandlerArgumentPlaceholderOperation operation, int? captureIdForResult) @@ -6846,12 +6847,12 @@ public override IOperation VisitInterpolatedString(IInterpolatedStringOperation public override IOperation VisitInterpolatedStringText(IInterpolatedStringTextOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitInterpolation(IInterpolationOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitNameOf(INameOfOperation operation, int? captureIdForResult) @@ -7298,7 +7299,7 @@ IReDimClauseOperation visitReDimClause(IReDimClauseOperation clause) public override IOperation VisitReDimClause(IReDimClauseOperation operation, int? argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitTranslatedQuery(ITranslatedQueryOperation operation, int? captureIdForResult) @@ -7632,17 +7633,17 @@ private void VisitUsingVariableDeclarationOperation(IUsingDeclarationOperation o public override IOperation DefaultVisit(IOperation operation, int? captureIdForResult) { // this should never reach, otherwise, there is missing override for IOperation type - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitArgument(IArgumentOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitUsingDeclaration(IUsingDeclarationOperation operation, int? captureIdForResult) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitWith(IWithOperation operation, int? captureIdForResult) @@ -7824,5 +7825,10 @@ static bool setsAllProperties(ImmutableArray initializers, IEnumerab return set.Count == properties.Count(); } } + + public override IOperation VisitAttribute(IAttributeOperation operation, int? captureIdForResult) + { + return new AttributeOperation(Visit(operation.Operation, captureIdForResult)!, semanticModel: null, operation.Syntax, IsImplicit(operation)); + } } } diff --git a/src/Compilers/Core/Portable/Operations/Operation.cs b/src/Compilers/Core/Portable/Operations/Operation.cs index 0eac58b1f866a..454b41ffe0dd7 100644 --- a/src/Compilers/Core/Portable/Operations/Operation.cs +++ b/src/Compilers/Core/Portable/Operations/Operation.cs @@ -30,16 +30,9 @@ protected Operation(SemanticModel? semanticModel, SyntaxNode syntax, bool isImpl #if DEBUG if (semanticModel != null) { - Debug.Assert(semanticModel.ContainingModelOrSelf != null); - if (semanticModel.IsSpeculativeSemanticModel) - { - Debug.Assert(semanticModel.ContainingModelOrSelf == semanticModel); - } - else - { - Debug.Assert(semanticModel.ContainingModelOrSelf != semanticModel); - Debug.Assert(semanticModel.ContainingModelOrSelf.ContainingModelOrSelf == semanticModel.ContainingModelOrSelf); - } + Debug.Assert(semanticModel.ContainingPublicModelOrSelf != null); + Debug.Assert(semanticModel.ContainingPublicModelOrSelf != semanticModel); + Debug.Assert(semanticModel.ContainingPublicModelOrSelf.ContainingPublicModelOrSelf == semanticModel.ContainingPublicModelOrSelf); } #endif _owningSemanticModelOpt = semanticModel; @@ -128,7 +121,7 @@ public Optional ConstantValue /// internal abstract (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex); - SemanticModel? IOperation.SemanticModel => _owningSemanticModelOpt?.ContainingModelOrSelf; + SemanticModel? IOperation.SemanticModel => _owningSemanticModelOpt?.ContainingPublicModelOrSelf; /// /// Gets the owning semantic model for this operation node. diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index 83db655537285..fca0ca4eedbc8 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -49,22 +49,22 @@ public override IOperation VisitInvalid(IInvalidOperation operation, object? arg public override IOperation VisitFlowCapture(IFlowCaptureOperation operation, object? argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitIsNull(IIsNullOperation operation, object? argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitCaughtException(ICaughtExceptionOperation operation, object? argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override IOperation VisitStaticLocalInitializationSemaphore(IStaticLocalInitializationSemaphoreOperation operation, object? argument) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 80e42d3e1c826..3dea22c6f4959 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3349,4 +3349,21 @@ + + + + Represents the application of an attribute. + + Current usage: + (1) C# attribute application. + (2) VB attribute application. + + + + + + The operation representing the attribute. This can be a in non-error cases, or an in error cases. + + + 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/PEWriter/InheritedTypeParameter.cs b/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs index 703a2b1cc73e8..687e95c603693 100644 --- a/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs +++ b/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs @@ -225,7 +225,7 @@ public bool IsValueType public ITypeDefinition GetResolvedType(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public PrimitiveTypeCode TypeCode @@ -273,39 +273,39 @@ public bool MangleName public bool IsNested { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public bool IsSpecializedNested { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public ITypeReference UnspecializedVersion { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public bool IsNamespaceTypeReference { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public bool IsGenericTypeInstance { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index 6068d257f6cfe..871ed0ab201ef 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -210,7 +210,7 @@ public virtual void Visit(ILocalDefinition localDefinition) public virtual void Visit(IMarshallingInformation marshallingInformation) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public virtual void Visit(MetadataConstant constant) diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs index 927a4073e24d7..4aafce9c3211d 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs @@ -301,7 +301,7 @@ private static SignatureTypeCode GetConstantTypeCode(object value) return SignatureTypeCode.Single; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #region ImportScope diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index f952948663bbf..8ba05edd5864d 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -1099,7 +1099,7 @@ private BlobHandle GetMemberReferenceSignatureHandle(ITypeMemberReference member { IFieldReference fieldReference => this.GetFieldSignatureIndex(fieldReference), IMethodReference methodReference => this.GetMethodSignatureHandle(methodReference), - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } @@ -1587,7 +1587,7 @@ private EntityHandle GetDeclaringTypeOrMethodHandle(IGenericParameter genPar) return GetMethodDefinitionHandle(genMethPar.DefiningMethod); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private TypeReferenceHandle GetTypeReferenceHandle(ITypeReference typeReference) @@ -1678,7 +1678,7 @@ internal EntityHandle GetDefinitionHandle(IDefinition definition) IFieldDefinition fieldDef => GetFieldDefinitionHandle(fieldDef), IEventDefinition eventDef => GetEventDefinitionHandle(eventDef), IPropertyDefinition propertyDef => GetPropertyDefIndex(propertyDef), - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } diff --git a/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs b/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs index 7207a9235c20a..5d3d9b805dbe9 100644 --- a/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs +++ b/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs @@ -44,17 +44,17 @@ ITypeReference IModifiedTypeReference.UnmodifiedType bool ITypeReference.IsEnum { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } bool ITypeReference.IsValueType { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } ITypeDefinition ITypeReference.GetResolvedType(EmitContext context) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } PrimitiveTypeCode ITypeReference.TypeCode @@ -64,7 +64,7 @@ PrimitiveTypeCode ITypeReference.TypeCode TypeDefinitionHandle ITypeReference.TypeDef { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } IEnumerable IReference.GetAttributes(EmitContext context) @@ -150,13 +150,13 @@ void IReference.Dispatch(MetadataVisitor visitor) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs index e0764b747d7ec..254dc23668a02 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs @@ -125,7 +125,7 @@ protected override void ProcessMethodBody(IMethodDefinition method) } else if (!metadataWriter.MetadataOnly) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs b/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs index bd12944232851..797aa914a236c 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReturnValueParameter.cs @@ -117,13 +117,13 @@ public IDefinition AsDefinition(EmitContext context) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs index 42c976c9a2d65..fcfcabeb35693 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleStaticConstructor.cs @@ -168,13 +168,13 @@ public RootModuleStaticConstructor(ITypeDefinition containingTypeDefinition, Imm public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs index dd6391debef0f..0a5c4c0cc81d4 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs @@ -195,7 +195,7 @@ public bool IsNested IEnumerable ITypeDefinition.GenericParameters { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } ushort ITypeDefinition.GenericParameterCount @@ -208,22 +208,22 @@ ushort ITypeDefinition.GenericParameterCount IEnumerable ITypeDefinition.SecurityAttributes { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } void IReference.Dispatch(MetadataVisitor visitor) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } bool ITypeReference.IsEnum { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } bool ITypeReference.IsValueType { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } ITypeDefinition ITypeReference.GetResolvedType(EmitContext context) @@ -233,12 +233,12 @@ ITypeDefinition ITypeReference.GetResolvedType(EmitContext context) PrimitiveTypeCode ITypeReference.TypeCode { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } ushort INamedTypeReference.GenericParameterCount { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } IUnitReference INamespaceTypeReference.GetUnit(EmitContext context) @@ -327,13 +327,13 @@ IDefinition IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs b/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs index 361f5663f06ac..fbe377f8b1775 100644 --- a/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs +++ b/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs @@ -43,12 +43,12 @@ public SequencePoint( public override int GetHashCode() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override bool Equals(object? obj) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private string GetDebuggerDisplay() diff --git a/src/Compilers/Core/Portable/PEWriter/SourceSpan.cs b/src/Compilers/Core/Portable/PEWriter/SourceSpan.cs index 2d79826af023a..755a2ca3183c1 100644 --- a/src/Compilers/Core/Portable/PEWriter/SourceSpan.cs +++ b/src/Compilers/Core/Portable/PEWriter/SourceSpan.cs @@ -36,12 +36,12 @@ public SourceSpan( public override int GetHashCode() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override bool Equals(object? obj) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private string GetDebuggerDisplay() diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index f13aa08394547..c9d3372ccfdbf 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,6 +21,15 @@ Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.None = 0 -> Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.ScopedRef = 1 -> Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.ScopedValue = 2 -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.SymbolDisplayLocalOptions.IncludeModifiers = 4 -> Microsoft.CodeAnalysis.SymbolDisplayLocalOptions +Microsoft.CodeAnalysis.SymbolDisplayParameterOptions.IncludeModifiers = 2 -> Microsoft.CodeAnalysis.SymbolDisplayParameterOptions override sealed Microsoft.CodeAnalysis.CompilationOptions.GetHashCode() -> int +static Microsoft.CodeAnalysis.Location.Create(string! filePath, Microsoft.CodeAnalysis.Text.TextSpan textSpan, Microsoft.CodeAnalysis.Text.LinePositionSpan lineSpan, string! mappedFilePath, Microsoft.CodeAnalysis.Text.LinePositionSpan mappedLineSpan) -> Microsoft.CodeAnalysis.Location! static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.Action! onDispose) -> Microsoft.CodeAnalysis.ModuleMetadata! Microsoft.CodeAnalysis.INamedTypeSymbol.IsFileLocal.get -> bool +Microsoft.CodeAnalysis.OperationKind.Attribute = 125 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.IAttributeOperation +Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! +static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IAttributeOperation! attribute, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation) -> void +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? \ No newline at end of file diff --git a/src/Compilers/Core/Portable/ReferenceManager/AssemblyDataForAssemblyBeingBuilt.cs b/src/Compilers/Core/Portable/ReferenceManager/AssemblyDataForAssemblyBeingBuilt.cs index 16a760b05b296..e7d4513b0b51a 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/AssemblyDataForAssemblyBeingBuilt.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/AssemblyDataForAssemblyBeingBuilt.cs @@ -70,7 +70,7 @@ public override IEnumerable AvailableSymbols { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -104,14 +104,14 @@ public override AssemblyReferenceBinding[] BindAssemblyReferences( public override bool IsMatchingAssembly(TAssemblySymbol? assembly) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override bool ContainsNoPiaLocalTypes { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs index de55a648d2e93..76e3aaa81f091 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs @@ -966,7 +966,7 @@ internal static AssemblyReferenceBinding ResolveReferencedAssembly( continue; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 1a8e37a1d2e3e..9cc79e692db6f 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -26,7 +26,7 @@ namespace Roslyn.Utilities using Resources = WorkspacesResources; #endif - using EncodingKind = ObjectWriter.EncodingKind; + using TypeCode = ObjectWriter.TypeCode; /// /// An that deserializes objects from a byte stream. @@ -39,7 +39,7 @@ internal sealed partial class ObjectReader : IDisposable /// this version, just change VersionByte2. /// internal const byte VersionByte1 = 0b10101010; - internal const byte VersionByte2 = 0b00001011; + internal const byte VersionByte2 = 0b00001100; private readonly BinaryReader _reader; private readonly CancellationToken _cancellationToken; @@ -230,96 +230,86 @@ public object ReadValue() private object ReadValueWorker() { - var kind = (EncodingKind)_reader.ReadByte(); - switch (kind) - { - case EncodingKind.Null: return null; - case EncodingKind.Boolean_True: return true; - case EncodingKind.Boolean_False: return false; - case EncodingKind.Int8: return _reader.ReadSByte(); - case EncodingKind.UInt8: return _reader.ReadByte(); - case EncodingKind.Int16: return _reader.ReadInt16(); - case EncodingKind.UInt16: return _reader.ReadUInt16(); - case EncodingKind.Int32: return _reader.ReadInt32(); - case EncodingKind.Int32_1Byte: return (int)_reader.ReadByte(); - case EncodingKind.Int32_2Bytes: return (int)_reader.ReadUInt16(); - case EncodingKind.Int32_0: - case EncodingKind.Int32_1: - case EncodingKind.Int32_2: - case EncodingKind.Int32_3: - case EncodingKind.Int32_4: - case EncodingKind.Int32_5: - case EncodingKind.Int32_6: - case EncodingKind.Int32_7: - case EncodingKind.Int32_8: - case EncodingKind.Int32_9: - case EncodingKind.Int32_10: - return (int)kind - (int)EncodingKind.Int32_0; - case EncodingKind.UInt32: return _reader.ReadUInt32(); - case EncodingKind.UInt32_1Byte: return (uint)_reader.ReadByte(); - case EncodingKind.UInt32_2Bytes: return (uint)_reader.ReadUInt16(); - case EncodingKind.UInt32_0: - case EncodingKind.UInt32_1: - case EncodingKind.UInt32_2: - case EncodingKind.UInt32_3: - case EncodingKind.UInt32_4: - case EncodingKind.UInt32_5: - case EncodingKind.UInt32_6: - case EncodingKind.UInt32_7: - case EncodingKind.UInt32_8: - case EncodingKind.UInt32_9: - case EncodingKind.UInt32_10: - return (uint)((int)kind - (int)EncodingKind.UInt32_0); - case EncodingKind.Int64: return _reader.ReadInt64(); - case EncodingKind.UInt64: return _reader.ReadUInt64(); - case EncodingKind.Float4: return _reader.ReadSingle(); - case EncodingKind.Float8: return _reader.ReadDouble(); - case EncodingKind.Decimal: return _reader.ReadDecimal(); - case EncodingKind.Char: + var code = (TypeCode)_reader.ReadByte(); + switch (code) + { + case TypeCode.Null: return null; + case TypeCode.Boolean_True: return true; + case TypeCode.Boolean_False: return false; + case TypeCode.Int8: return _reader.ReadSByte(); + case TypeCode.UInt8: return _reader.ReadByte(); + case TypeCode.Int16: return _reader.ReadInt16(); + case TypeCode.UInt16: return _reader.ReadUInt16(); + case TypeCode.Int32: return _reader.ReadInt32(); + case TypeCode.Int32_1Byte: return (int)_reader.ReadByte(); + case TypeCode.Int32_2Bytes: return (int)_reader.ReadUInt16(); + case TypeCode.Int32_0: + case TypeCode.Int32_1: + case TypeCode.Int32_2: + case TypeCode.Int32_3: + case TypeCode.Int32_4: + case TypeCode.Int32_5: + case TypeCode.Int32_6: + case TypeCode.Int32_7: + case TypeCode.Int32_8: + case TypeCode.Int32_9: + case TypeCode.Int32_10: + return (int)code - (int)TypeCode.Int32_0; + case TypeCode.UInt32: return _reader.ReadUInt32(); + case TypeCode.UInt32_1Byte: return (uint)_reader.ReadByte(); + case TypeCode.UInt32_2Bytes: return (uint)_reader.ReadUInt16(); + case TypeCode.UInt32_0: + case TypeCode.UInt32_1: + case TypeCode.UInt32_2: + case TypeCode.UInt32_3: + case TypeCode.UInt32_4: + case TypeCode.UInt32_5: + case TypeCode.UInt32_6: + case TypeCode.UInt32_7: + case TypeCode.UInt32_8: + case TypeCode.UInt32_9: + case TypeCode.UInt32_10: + return (uint)((int)code - (int)TypeCode.UInt32_0); + case TypeCode.Int64: return _reader.ReadInt64(); + case TypeCode.UInt64: return _reader.ReadUInt64(); + case TypeCode.Float4: return _reader.ReadSingle(); + case TypeCode.Float8: return _reader.ReadDouble(); + case TypeCode.Decimal: return _reader.ReadDecimal(); + case TypeCode.Char: // read as ushort because BinaryWriter fails on chars that are unicode surrogates return (char)_reader.ReadUInt16(); - case EncodingKind.StringUtf8: - case EncodingKind.StringUtf16: - case EncodingKind.StringRef_4Bytes: - case EncodingKind.StringRef_1Byte: - case EncodingKind.StringRef_2Bytes: - return ReadStringValue(kind); - case EncodingKind.ObjectRef_4Bytes: return _objectReferenceMap.GetValue(_reader.ReadInt32()); - case EncodingKind.ObjectRef_1Byte: return _objectReferenceMap.GetValue(_reader.ReadByte()); - case EncodingKind.ObjectRef_2Bytes: return _objectReferenceMap.GetValue(_reader.ReadUInt16()); - case EncodingKind.Object: return ReadObject(); - case EncodingKind.DateTime: return DateTime.FromBinary(_reader.ReadInt64()); - case EncodingKind.Array: - case EncodingKind.Array_0: - case EncodingKind.Array_1: - case EncodingKind.Array_2: - case EncodingKind.Array_3: - return ReadArray(kind); - - case EncodingKind.EncodingName: return Encoding.GetEncoding(ReadString()); - case EncodingKind.EncodingUtf8: return s_encodingUtf8; - case EncodingKind.EncodingUtf8_BOM: return Encoding.UTF8; - case EncodingKind.EncodingUtf32_BE: return s_encodingUtf32_BE; - case EncodingKind.EncodingUtf32_BE_BOM: return s_encodingUtf32_BE_BOM; - case EncodingKind.EncodingUtf32_LE: return s_encodingUtf32_LE; - case EncodingKind.EncodingUtf32_LE_BOM: return Encoding.UTF32; - case EncodingKind.EncodingUnicode_BE: return s_encodingUnicode_BE; - case EncodingKind.EncodingUnicode_BE_BOM: return Encoding.BigEndianUnicode; - case EncodingKind.EncodingUnicode_LE: return s_encodingUnicode_LE; - case EncodingKind.EncodingUnicode_LE_BOM: return Encoding.Unicode; + case TypeCode.StringUtf8: + case TypeCode.StringUtf16: + case TypeCode.StringRef_4Bytes: + case TypeCode.StringRef_1Byte: + case TypeCode.StringRef_2Bytes: + return ReadStringValue(code); + case TypeCode.ObjectRef_4Bytes: return _objectReferenceMap.GetValue(_reader.ReadInt32()); + case TypeCode.ObjectRef_1Byte: return _objectReferenceMap.GetValue(_reader.ReadByte()); + case TypeCode.ObjectRef_2Bytes: return _objectReferenceMap.GetValue(_reader.ReadUInt16()); + case TypeCode.Object: return ReadObject(); + case TypeCode.DateTime: return DateTime.FromBinary(_reader.ReadInt64()); + case TypeCode.Array: + case TypeCode.Array_0: + case TypeCode.Array_1: + case TypeCode.Array_2: + case TypeCode.Array_3: + return ReadArray(code); + + case TypeCode.EncodingName: + return Encoding.GetEncoding(ReadString()); + + case >= TypeCode.FirstWellKnownTextEncoding and <= TypeCode.LastWellKnownTextEncoding: + return ObjectWriter.ToEncodingKind(code).GetEncoding(); + + case TypeCode.EncodingCodePage: + return Encoding.GetEncoding(ReadInt32()); default: - throw ExceptionUtilities.UnexpectedValue(kind); + throw ExceptionUtilities.UnexpectedValue(code); } } - private static readonly Encoding s_encodingUtf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - private static readonly Encoding s_encodingUtf32_BE = new UTF32Encoding(bigEndian: true, byteOrderMark: false); - private static readonly Encoding s_encodingUtf32_BE_BOM = new UTF32Encoding(bigEndian: true, byteOrderMark: true); - private static readonly Encoding s_encodingUtf32_LE = new UTF32Encoding(bigEndian: false, byteOrderMark: false); - private static readonly Encoding s_encodingUnicode_BE = new UnicodeEncoding(bigEndian: true, byteOrderMark: false); - private static readonly Encoding s_encodingUnicode_LE = new UnicodeEncoding(bigEndian: false, byteOrderMark: false); - /// /// A reference-id to object map, that can share base data efficiently. /// @@ -391,25 +381,25 @@ internal uint ReadCompressedUInt() private string ReadStringValue() { - var kind = (EncodingKind)_reader.ReadByte(); - return kind == EncodingKind.Null ? null : ReadStringValue(kind); + var kind = (TypeCode)_reader.ReadByte(); + return kind == TypeCode.Null ? null : ReadStringValue(kind); } - private string ReadStringValue(EncodingKind kind) + private string ReadStringValue(TypeCode kind) { switch (kind) { - case EncodingKind.StringRef_1Byte: + case TypeCode.StringRef_1Byte: return _stringReferenceMap.GetValue(_reader.ReadByte()); - case EncodingKind.StringRef_2Bytes: + case TypeCode.StringRef_2Bytes: return _stringReferenceMap.GetValue(_reader.ReadUInt16()); - case EncodingKind.StringRef_4Bytes: + case TypeCode.StringRef_4Bytes: return _stringReferenceMap.GetValue(_reader.ReadInt32()); - case EncodingKind.StringUtf16: - case EncodingKind.StringUtf8: + case TypeCode.StringUtf16: + case TypeCode.StringUtf8: return ReadStringLiteral(kind); default: @@ -417,10 +407,10 @@ private string ReadStringValue(EncodingKind kind) } } - private unsafe string ReadStringLiteral(EncodingKind kind) + private unsafe string ReadStringLiteral(TypeCode kind) { string value; - if (kind == EncodingKind.StringUtf8) + if (kind == TypeCode.StringUtf8) { value = _reader.ReadString(); } @@ -439,21 +429,21 @@ private unsafe string ReadStringLiteral(EncodingKind kind) return value; } - private Array ReadArray(EncodingKind kind) + private Array ReadArray(TypeCode kind) { int length; switch (kind) { - case EncodingKind.Array_0: + case TypeCode.Array_0: length = 0; break; - case EncodingKind.Array_1: + case TypeCode.Array_1: length = 1; break; - case EncodingKind.Array_2: + case TypeCode.Array_2: length = 2; break; - case EncodingKind.Array_3: + case TypeCode.Array_3: length = 3; break; default: @@ -462,7 +452,7 @@ private Array ReadArray(EncodingKind kind) } // SUBTLE: If it was a primitive array, only the EncodingKind byte of the element type was written, instead of encoding as a type. - var elementKind = (EncodingKind)_reader.ReadByte(); + var elementKind = (TypeCode)_reader.ReadByte(); var elementType = ObjectWriter.s_reverseTypeMap[(int)elementKind]; if (elementType != null) @@ -487,7 +477,7 @@ private Array ReadArray(EncodingKind kind) } } - private Array ReadPrimitiveTypeArrayElements(Type type, EncodingKind kind, int length) + private Array ReadPrimitiveTypeArrayElements(Type type, TypeCode kind, int length) { Debug.Assert(ObjectWriter.s_reverseTypeMap[(int)kind] == type); @@ -503,16 +493,16 @@ private Array ReadPrimitiveTypeArrayElements(Type type, EncodingKind kind, int l // otherwise, read elements directly from underlying binary writer switch (kind) { - case EncodingKind.Int8: return ReadInt8ArrayElements(CreateArray(length)); - case EncodingKind.Int16: return ReadInt16ArrayElements(CreateArray(length)); - case EncodingKind.Int32: return ReadInt32ArrayElements(CreateArray(length)); - case EncodingKind.Int64: return ReadInt64ArrayElements(CreateArray(length)); - case EncodingKind.UInt16: return ReadUInt16ArrayElements(CreateArray(length)); - case EncodingKind.UInt32: return ReadUInt32ArrayElements(CreateArray(length)); - case EncodingKind.UInt64: return ReadUInt64ArrayElements(CreateArray(length)); - case EncodingKind.Float4: return ReadFloat4ArrayElements(CreateArray(length)); - case EncodingKind.Float8: return ReadFloat8ArrayElements(CreateArray(length)); - case EncodingKind.Decimal: return ReadDecimalArrayElements(CreateArray(length)); + case TypeCode.Int8: return ReadInt8ArrayElements(CreateArray(length)); + case TypeCode.Int16: return ReadInt16ArrayElements(CreateArray(length)); + case TypeCode.Int32: return ReadInt32ArrayElements(CreateArray(length)); + case TypeCode.Int64: return ReadInt64ArrayElements(CreateArray(length)); + case TypeCode.UInt16: return ReadUInt16ArrayElements(CreateArray(length)); + case TypeCode.UInt32: return ReadUInt32ArrayElements(CreateArray(length)); + case TypeCode.UInt64: return ReadUInt64ArrayElements(CreateArray(length)); + case TypeCode.Float4: return ReadFloat4ArrayElements(CreateArray(length)); + case TypeCode.Float8: return ReadFloat8ArrayElements(CreateArray(length)); + case TypeCode.Decimal: return ReadDecimalArrayElements(CreateArray(length)); default: throw ExceptionUtilities.UnexpectedValue(kind); } diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index e694088d292c4..9f7769a485bf4 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -155,7 +155,7 @@ public void WriteValue(object? value) if (value == null) { - _writer.Write((byte)EncodingKind.Null); + _writer.Write((byte)TypeCode.Null); return; } @@ -180,46 +180,46 @@ public void WriteValue(object? value) } else if (value.GetType() == typeof(double)) { - _writer.Write((byte)EncodingKind.Float8); + _writer.Write((byte)TypeCode.Float8); _writer.Write((double)value); } else if (value.GetType() == typeof(bool)) { - _writer.Write((byte)((bool)value ? EncodingKind.Boolean_True : EncodingKind.Boolean_False)); + _writer.Write((byte)((bool)value ? TypeCode.Boolean_True : TypeCode.Boolean_False)); } else if (value.GetType() == typeof(char)) { - _writer.Write((byte)EncodingKind.Char); + _writer.Write((byte)TypeCode.Char); _writer.Write((ushort)(char)value); // written as ushort because BinaryWriter fails on chars that are unicode surrogates } else if (value.GetType() == typeof(byte)) { - _writer.Write((byte)EncodingKind.UInt8); + _writer.Write((byte)TypeCode.UInt8); _writer.Write((byte)value); } else if (value.GetType() == typeof(short)) { - _writer.Write((byte)EncodingKind.Int16); + _writer.Write((byte)TypeCode.Int16); _writer.Write((short)value); } else if (value.GetType() == typeof(long)) { - _writer.Write((byte)EncodingKind.Int64); + _writer.Write((byte)TypeCode.Int64); _writer.Write((long)value); } else if (value.GetType() == typeof(sbyte)) { - _writer.Write((byte)EncodingKind.Int8); + _writer.Write((byte)TypeCode.Int8); _writer.Write((sbyte)value); } else if (value.GetType() == typeof(float)) { - _writer.Write((byte)EncodingKind.Float4); + _writer.Write((byte)TypeCode.Float4); _writer.Write((float)value); } else if (value.GetType() == typeof(ushort)) { - _writer.Write((byte)EncodingKind.UInt16); + _writer.Write((byte)TypeCode.UInt16); _writer.Write((ushort)value); } else if (value.GetType() == typeof(uint)) @@ -228,7 +228,7 @@ public void WriteValue(object? value) } else if (value.GetType() == typeof(ulong)) { - _writer.Write((byte)EncodingKind.UInt64); + _writer.Write((byte)TypeCode.UInt64); _writer.Write((ulong)value); } else @@ -238,12 +238,12 @@ public void WriteValue(object? value) } else if (value.GetType() == typeof(decimal)) { - _writer.Write((byte)EncodingKind.Decimal); + _writer.Write((byte)TypeCode.Decimal); _writer.Write((decimal)value); } else if (value.GetType() == typeof(DateTime)) { - _writer.Write((byte)EncodingKind.DateTime); + _writer.Write((byte)TypeCode.DateTime); _writer.Write(((DateTime)value).ToBinary()); } else if (value.GetType() == typeof(string)) @@ -282,27 +282,27 @@ public void WriteValue(ReadOnlySpan span) switch (length) { case 0: - _writer.Write((byte)EncodingKind.Array_0); + _writer.Write((byte)TypeCode.Array_0); break; case 1: - _writer.Write((byte)EncodingKind.Array_1); + _writer.Write((byte)TypeCode.Array_1); break; case 2: - _writer.Write((byte)EncodingKind.Array_2); + _writer.Write((byte)TypeCode.Array_2); break; case 3: - _writer.Write((byte)EncodingKind.Array_3); + _writer.Write((byte)TypeCode.Array_3); break; default: - _writer.Write((byte)EncodingKind.Array); + _writer.Write((byte)TypeCode.Array); WriteCompressedUInt((uint)length); break; } var elementType = typeof(byte); - Debug.Assert(s_typeMap[elementType] == EncodingKind.UInt8); + Debug.Assert(s_typeMap[elementType] == TypeCode.UInt8); - WritePrimitiveType(elementType, EncodingKind.UInt8); + WritePrimitiveType(elementType, TypeCode.UInt8); #if NETCOREAPP _writer.Write(span); @@ -324,7 +324,7 @@ public void WriteValue(IObjectWritable? value) { if (value == null) { - _writer.Write((byte)EncodingKind.Null); + _writer.Write((byte)TypeCode.Null); return; } @@ -335,21 +335,21 @@ private void WriteEncodedInt32(int v) { if (v >= 0 && v <= 10) { - _writer.Write((byte)((int)EncodingKind.Int32_0 + v)); + _writer.Write((byte)((int)TypeCode.Int32_0 + v)); } else if (v >= 0 && v < byte.MaxValue) { - _writer.Write((byte)EncodingKind.Int32_1Byte); + _writer.Write((byte)TypeCode.Int32_1Byte); _writer.Write((byte)v); } else if (v >= 0 && v < ushort.MaxValue) { - _writer.Write((byte)EncodingKind.Int32_2Bytes); + _writer.Write((byte)TypeCode.Int32_2Bytes); _writer.Write((ushort)v); } else { - _writer.Write((byte)EncodingKind.Int32); + _writer.Write((byte)TypeCode.Int32); _writer.Write(v); } } @@ -358,21 +358,21 @@ private void WriteEncodedUInt32(uint v) { if (v >= 0 && v <= 10) { - _writer.Write((byte)((int)EncodingKind.UInt32_0 + v)); + _writer.Write((byte)((int)TypeCode.UInt32_0 + v)); } else if (v >= 0 && v < byte.MaxValue) { - _writer.Write((byte)EncodingKind.UInt32_1Byte); + _writer.Write((byte)TypeCode.UInt32_1Byte); _writer.Write((byte)v); } else if (v >= 0 && v < ushort.MaxValue) { - _writer.Write((byte)EncodingKind.UInt32_2Bytes); + _writer.Write((byte)TypeCode.UInt32_2Bytes); _writer.Write((ushort)v); } else { - _writer.Write((byte)EncodingKind.UInt32); + _writer.Write((byte)TypeCode.UInt32); _writer.Write(v); } } @@ -473,7 +473,7 @@ private unsafe void WriteStringValue(string? value) { if (value == null) { - _writer.Write((byte)EncodingKind.Null); + _writer.Write((byte)TypeCode.Null); } else { @@ -482,17 +482,17 @@ private unsafe void WriteStringValue(string? value) Debug.Assert(id >= 0); if (id <= byte.MaxValue) { - _writer.Write((byte)EncodingKind.StringRef_1Byte); + _writer.Write((byte)TypeCode.StringRef_1Byte); _writer.Write((byte)id); } else if (id <= ushort.MaxValue) { - _writer.Write((byte)EncodingKind.StringRef_2Bytes); + _writer.Write((byte)TypeCode.StringRef_2Bytes); _writer.Write((ushort)id); } else { - _writer.Write((byte)EncodingKind.StringRef_4Bytes); + _writer.Write((byte)TypeCode.StringRef_4Bytes); _writer.Write(id); } } @@ -505,12 +505,12 @@ private unsafe void WriteStringValue(string? value) // Usual case - the string can be encoded as UTF-8: // We can use the UTF-8 encoding of the binary writer. - _writer.Write((byte)EncodingKind.StringUtf8); + _writer.Write((byte)TypeCode.StringUtf8); _writer.Write(value); } else { - _writer.Write((byte)EncodingKind.StringUtf16); + _writer.Write((byte)TypeCode.StringUtf16); // This is rare, just allocate UTF16 bytes for simplicity. byte[] bytes = new byte[(uint)value.Length * sizeof(char)]; @@ -533,19 +533,19 @@ private void WriteArray(Array array) switch (length) { case 0: - _writer.Write((byte)EncodingKind.Array_0); + _writer.Write((byte)TypeCode.Array_0); break; case 1: - _writer.Write((byte)EncodingKind.Array_1); + _writer.Write((byte)TypeCode.Array_1); break; case 2: - _writer.Write((byte)EncodingKind.Array_2); + _writer.Write((byte)TypeCode.Array_2); break; case 3: - _writer.Write((byte)EncodingKind.Array_3); + _writer.Write((byte)TypeCode.Array_3); break; default: - _writer.Write((byte)EncodingKind.Array); + _writer.Write((byte)TypeCode.Array); this.WriteCompressedUInt((uint)length); break; } @@ -607,7 +607,7 @@ private void WriteArrayValues(Array array) } } - private void WritePrimitiveTypeArrayElements(Type type, EncodingKind kind, Array instance) + private void WritePrimitiveTypeArrayElements(Type type, TypeCode kind, Array instance) { Debug.Assert(s_typeMap[type] == kind); @@ -636,34 +636,34 @@ private void WritePrimitiveTypeArrayElements(Type type, EncodingKind kind, Array // otherwise, write elements directly to underlying binary writer switch (kind) { - case EncodingKind.Int8: + case TypeCode.Int8: WriteInt8ArrayElements((sbyte[])instance); return; - case EncodingKind.Int16: + case TypeCode.Int16: WriteInt16ArrayElements((short[])instance); return; - case EncodingKind.Int32: + case TypeCode.Int32: WriteInt32ArrayElements((int[])instance); return; - case EncodingKind.Int64: + case TypeCode.Int64: WriteInt64ArrayElements((long[])instance); return; - case EncodingKind.UInt16: + case TypeCode.UInt16: WriteUInt16ArrayElements((ushort[])instance); return; - case EncodingKind.UInt32: + case TypeCode.UInt32: WriteUInt32ArrayElements((uint[])instance); return; - case EncodingKind.UInt64: + case TypeCode.UInt64: WriteUInt64ArrayElements((ulong[])instance); return; - case EncodingKind.Float4: + case TypeCode.Float4: WriteFloat4ArrayElements((float[])instance); return; - case EncodingKind.Float8: + case TypeCode.Float8: WriteFloat8ArrayElements((double[])instance); return; - case EncodingKind.Decimal: + case TypeCode.Decimal: WriteDecimalArrayElements((decimal[])instance); return; default: @@ -776,7 +776,7 @@ private void WriteFloat8ArrayElements(double[] array) } } - private void WritePrimitiveType(Type type, EncodingKind kind) + private void WritePrimitiveType(Type type, TypeCode kind) { Debug.Assert(s_typeMap[type] == kind); _writer.Write((byte)kind); @@ -784,66 +784,36 @@ private void WritePrimitiveType(Type type, EncodingKind kind) public void WriteType(Type type) { - _writer.Write((byte)EncodingKind.Type); + _writer.Write((byte)TypeCode.Type); this.WriteString(type.AssemblyQualifiedName); } private void WriteKnownType(Type type) { - _writer.Write((byte)EncodingKind.Type); + _writer.Write((byte)TypeCode.Type); this.WriteInt32(_binderSnapshot.GetTypeId(type)); } public void WriteEncoding(Encoding? encoding) { - var kind = GetEncodingKind(encoding); - WriteByte((byte)kind); - - if (kind == EncodingKind.EncodingName) + if (encoding == null) { - WriteString(encoding!.WebName); + WriteByte((byte)TypeCode.Null); } - } - - private static EncodingKind GetEncodingKind(Encoding? encoding) - { - if (encoding is null) + else if (encoding.TryGetEncodingKind(out var kind)) { - return EncodingKind.Null; + WriteByte((byte)ToTypeCode(kind)); } - - switch (encoding.CodePage) + else if (encoding.CodePage > 0) { - case 1200: - Debug.Assert(HasPreamble(Encoding.Unicode)); - return (encoding.Equals(Encoding.Unicode) || HasPreamble(encoding)) ? EncodingKind.EncodingUnicode_LE_BOM : EncodingKind.EncodingUnicode_LE; - - case 1201: - Debug.Assert(HasPreamble(Encoding.BigEndianUnicode)); - return (encoding.Equals(Encoding.BigEndianUnicode) || HasPreamble(encoding)) ? EncodingKind.EncodingUnicode_BE_BOM : EncodingKind.EncodingUnicode_BE; - - case 12000: - Debug.Assert(HasPreamble(Encoding.UTF32)); - return (encoding.Equals(Encoding.UTF32) || HasPreamble(encoding)) ? EncodingKind.EncodingUtf32_LE_BOM : EncodingKind.EncodingUtf32_LE; - - case 12001: - Debug.Assert(HasPreamble(Encoding.UTF32)); - return (encoding.Equals(Encoding.UTF32) || HasPreamble(encoding)) ? EncodingKind.EncodingUtf32_BE_BOM : EncodingKind.EncodingUtf32_BE; - - case 65001: - Debug.Assert(HasPreamble(Encoding.UTF8)); - return (encoding.Equals(Encoding.UTF8) || HasPreamble(encoding)) ? EncodingKind.EncodingUtf8_BOM : EncodingKind.EncodingUtf8; - - default: - return EncodingKind.EncodingName; + WriteByte((byte)TypeCode.EncodingCodePage); + WriteInt32(encoding.CodePage); + } + else + { + WriteByte((byte)TypeCode.EncodingName); + WriteString(encoding.WebName); } - - static bool HasPreamble(Encoding encoding) -#if NETCOREAPP - => !encoding.Preamble.IsEmpty; -#else - => !encoding.GetPreamble().IsEmpty(); -#endif } private void WriteObject(object instance, IObjectWritable? instanceAsWritable) @@ -859,17 +829,17 @@ private void WriteObject(object instance, IObjectWritable? instanceAsWritable) Debug.Assert(id >= 0); if (id <= byte.MaxValue) { - _writer.Write((byte)EncodingKind.ObjectRef_1Byte); + _writer.Write((byte)TypeCode.ObjectRef_1Byte); _writer.Write((byte)id); } else if (id <= ushort.MaxValue) { - _writer.Write((byte)EncodingKind.ObjectRef_2Bytes); + _writer.Write((byte)TypeCode.ObjectRef_2Bytes); _writer.Write((ushort)id); } else { - _writer.Write((byte)EncodingKind.ObjectRef_4Bytes); + _writer.Write((byte)TypeCode.ObjectRef_4Bytes); _writer.Write(id); } } @@ -926,7 +896,7 @@ private void WriteObjectWorker(IObjectWritable writable) _objectReferenceMap.Add(writable, writable.ShouldReuseInSerialization); // emit object header up front - _writer.Write((byte)EncodingKind.Object); + _writer.Write((byte)TypeCode.Object); // Directly write out the type-id for this object. i.e. no need to write out the 'Type' // tag since we just wrote out the 'Object' tag @@ -947,34 +917,34 @@ private static Exception NoSerializationWriterException(string typeName) // we have s_typeMap and s_reversedTypeMap since there is no bidirectional map in compiler // Note: s_typeMap is effectively immutable. However, for maximum perf we use mutable types because // they are used in hotspots. - internal static readonly Dictionary s_typeMap; + internal static readonly Dictionary s_typeMap; /// - /// Indexed by EncodingKind. + /// Indexed by . /// internal static readonly ImmutableArray s_reverseTypeMap; static ObjectWriter() { - s_typeMap = new Dictionary + s_typeMap = new Dictionary { - { typeof(bool), EncodingKind.BooleanType }, - { typeof(char), EncodingKind.Char }, - { typeof(string), EncodingKind.StringType }, - { typeof(sbyte), EncodingKind.Int8 }, - { typeof(short), EncodingKind.Int16 }, - { typeof(int), EncodingKind.Int32 }, - { typeof(long), EncodingKind.Int64 }, - { typeof(byte), EncodingKind.UInt8 }, - { typeof(ushort), EncodingKind.UInt16 }, - { typeof(uint), EncodingKind.UInt32 }, - { typeof(ulong), EncodingKind.UInt64 }, - { typeof(float), EncodingKind.Float4 }, - { typeof(double), EncodingKind.Float8 }, - { typeof(decimal), EncodingKind.Decimal }, + { typeof(bool), TypeCode.BooleanType }, + { typeof(char), TypeCode.Char }, + { typeof(string), TypeCode.StringType }, + { typeof(sbyte), TypeCode.Int8 }, + { typeof(short), TypeCode.Int16 }, + { typeof(int), TypeCode.Int32 }, + { typeof(long), TypeCode.Int64 }, + { typeof(byte), TypeCode.UInt8 }, + { typeof(ushort), TypeCode.UInt16 }, + { typeof(uint), TypeCode.UInt32 }, + { typeof(ulong), TypeCode.UInt64 }, + { typeof(float), TypeCode.Float4 }, + { typeof(double), TypeCode.Float8 }, + { typeof(decimal), TypeCode.Decimal }, }; - var temp = new Type[(int)EncodingKind.Last]; + var temp = new Type[(int)TypeCode.Last]; foreach (var kvp in s_typeMap) { @@ -1004,7 +974,7 @@ static ObjectWriter() /// internal const byte Byte4Marker = 2 << 6; - internal enum EncodingKind : byte + internal enum TypeCode : byte { /// /// The null value @@ -1306,19 +1276,30 @@ internal enum EncodingKind : byte /// EncodingName, - // well-known encodings (parameterized by BOM) - EncodingUtf8, - EncodingUtf8_BOM, - EncodingUtf32_BE, - EncodingUtf32_BE_BOM, - EncodingUtf32_LE, - EncodingUtf32_LE_BOM, - EncodingUnicode_BE, - EncodingUnicode_BE_BOM, - EncodingUnicode_LE, - EncodingUnicode_LE_BOM, + /// + /// Encoding serialized as . + /// + FirstWellKnownTextEncoding, + LastWellKnownTextEncoding = FirstWellKnownTextEncoding + EncodingExtensions.LastTextEncodingKind - EncodingExtensions.FirstTextEncodingKind, + + /// + /// Encoding serialized as . + /// + EncodingCodePage, Last, } + + internal static TypeCode ToTypeCode(TextEncodingKind kind) + { + Debug.Assert(kind is >= EncodingExtensions.FirstTextEncodingKind and <= EncodingExtensions.LastTextEncodingKind); + return TypeCode.FirstWellKnownTextEncoding + (byte)(kind - EncodingExtensions.FirstTextEncodingKind); + } + + internal static TextEncodingKind ToEncodingKind(TypeCode code) + { + Debug.Assert(code is >= TypeCode.FirstWellKnownTextEncoding and <= TypeCode.LastWellKnownTextEncoding); + return EncodingExtensions.FirstTextEncodingKind + (byte)(code - TypeCode.FirstWellKnownTextEncoding); + } } } diff --git a/src/Compilers/Core/Portable/Serialization/TextEncodingKind.cs b/src/Compilers/Core/Portable/Serialization/TextEncodingKind.cs new file mode 100644 index 0000000000000..aad44498e65ae --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/TextEncodingKind.cs @@ -0,0 +1,98 @@ +// 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.Text; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +/// +/// Well known encodings. Used to distinguish serialized encodings with BOM and without BOM. +/// +internal enum TextEncodingKind : byte +{ + None = 0, + EncodingUtf8 = 1, + EncodingUtf8_BOM = 2, + EncodingUtf32_BE = 3, + EncodingUtf32_BE_BOM = 4, + EncodingUtf32_LE = 5, + EncodingUtf32_LE_BOM = 6, + EncodingUnicode_BE = 7, + EncodingUnicode_BE_BOM = 8, + EncodingUnicode_LE = 9, + EncodingUnicode_LE_BOM = 10, +} + +internal static partial class EncodingExtensions +{ + internal const TextEncodingKind FirstTextEncodingKind = TextEncodingKind.EncodingUtf8; + internal const TextEncodingKind LastTextEncodingKind = TextEncodingKind.EncodingUnicode_LE_BOM; + + private static readonly Encoding s_encodingUtf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + private static readonly Encoding s_encodingUtf32_BE = new UTF32Encoding(bigEndian: true, byteOrderMark: false); + private static readonly Encoding s_encodingUtf32_BE_BOM = new UTF32Encoding(bigEndian: true, byteOrderMark: true); + private static readonly Encoding s_encodingUtf32_LE = new UTF32Encoding(bigEndian: false, byteOrderMark: false); + private static readonly Encoding s_encodingUnicode_BE = new UnicodeEncoding(bigEndian: true, byteOrderMark: false); + private static readonly Encoding s_encodingUnicode_LE = new UnicodeEncoding(bigEndian: false, byteOrderMark: false); + + public static Encoding GetEncoding(this TextEncodingKind kind) + => kind switch + { + TextEncodingKind.EncodingUtf8 => s_encodingUtf8, + TextEncodingKind.EncodingUtf8_BOM => Encoding.UTF8, + TextEncodingKind.EncodingUtf32_BE => s_encodingUtf32_BE, + TextEncodingKind.EncodingUtf32_BE_BOM => s_encodingUtf32_BE_BOM, + TextEncodingKind.EncodingUtf32_LE => s_encodingUtf32_LE, + TextEncodingKind.EncodingUtf32_LE_BOM => Encoding.UTF32, + TextEncodingKind.EncodingUnicode_BE => s_encodingUnicode_BE, + TextEncodingKind.EncodingUnicode_BE_BOM => Encoding.BigEndianUnicode, + TextEncodingKind.EncodingUnicode_LE => s_encodingUnicode_LE, + TextEncodingKind.EncodingUnicode_LE_BOM => Encoding.Unicode, + _ => throw ExceptionUtilities.UnexpectedValue(kind) + }; + + public static bool TryGetEncodingKind(this Encoding encoding, out TextEncodingKind kind) + { + switch (encoding.CodePage) + { + case 1200: + Debug.Assert(HasPreamble(Encoding.Unicode)); + kind = (encoding.Equals(Encoding.Unicode) || HasPreamble(encoding)) ? TextEncodingKind.EncodingUnicode_LE_BOM : TextEncodingKind.EncodingUnicode_LE; + return true; + + case 1201: + Debug.Assert(HasPreamble(Encoding.BigEndianUnicode)); + kind = (encoding.Equals(Encoding.BigEndianUnicode) || HasPreamble(encoding)) ? TextEncodingKind.EncodingUnicode_BE_BOM : TextEncodingKind.EncodingUnicode_BE; + return true; + + case 12000: + Debug.Assert(HasPreamble(Encoding.UTF32)); + kind = (encoding.Equals(Encoding.UTF32) || HasPreamble(encoding)) ? TextEncodingKind.EncodingUtf32_LE_BOM : TextEncodingKind.EncodingUtf32_LE; + return true; + + case 12001: + Debug.Assert(HasPreamble(Encoding.UTF32)); + kind = (encoding.Equals(Encoding.UTF32) || HasPreamble(encoding)) ? TextEncodingKind.EncodingUtf32_BE_BOM : TextEncodingKind.EncodingUtf32_BE; + return true; + + case 65001: + Debug.Assert(HasPreamble(Encoding.UTF8)); + kind = (encoding.Equals(Encoding.UTF8) || HasPreamble(encoding)) ? TextEncodingKind.EncodingUtf8_BOM : TextEncodingKind.EncodingUtf8; + return true; + + default: + kind = default; + return false; + } + } + + public static bool HasPreamble(this Encoding encoding) +#if NETCOREAPP + => !encoding.Preamble.IsEmpty; +#else + => !encoding.GetPreamble().IsEmpty(); +#endif +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/IncrementalWrapper.cs b/src/Compilers/Core/Portable/SourceGeneration/IncrementalWrapper.cs index 486c3758364cb..ed65f3f7db15f 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/IncrementalWrapper.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/IncrementalWrapper.cs @@ -26,8 +26,8 @@ public IncrementalGeneratorWrapper(IIncrementalGenerator generator) } // never used. Just for back compat with loading mechanism - void ISourceGenerator.Execute(GeneratorExecutionContext context) => throw ExceptionUtilities.Unreachable; + void ISourceGenerator.Execute(GeneratorExecutionContext context) => throw ExceptionUtilities.Unreachable(); - void ISourceGenerator.Initialize(GeneratorInitializationContext context) => throw ExceptionUtilities.Unreachable; + void ISourceGenerator.Initialize(GeneratorInitializationContext context) => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs index 8d7b13f033bec..de7c2371fc0bf 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs @@ -34,7 +34,7 @@ private InputNode(Func> getInput, Ac _getInput = getInput; _comparer = comparer ?? EqualityComparer.Default; _inputComparer = inputComparer ?? EqualityComparer.Default; - _registerOutput = registerOutput ?? (o => throw ExceptionUtilities.Unreachable); + _registerOutput = registerOutput ?? (o => throw ExceptionUtilities.Unreachable()); _name = name; } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs index c5c2aef5546fe..bad7be5fcfd2f 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs @@ -525,7 +525,7 @@ public bool Matches(TableEntry entry, IEqualityComparer equalityComparer) EntryState.Cached => s_allCachedEntries, EntryState.Modified => s_allModifiedEntries, EntryState.Removed => s_allRemovedEntries, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; public Enumerator GetEnumerator() diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs index 805b2494a179f..bc9c48d5a8dff 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @@ -85,11 +85,11 @@ public NodeStateTable UpdateStateTable(DriverStateTable.Builder graphSt return nodeTable.ToImmutableAndFree(); } - IIncrementalGeneratorNode IIncrementalGeneratorNode.WithComparer(IEqualityComparer comparer) => throw ExceptionUtilities.Unreachable; + IIncrementalGeneratorNode IIncrementalGeneratorNode.WithComparer(IEqualityComparer comparer) => throw ExceptionUtilities.Unreachable(); - public IIncrementalGeneratorNode<(IEnumerable, IEnumerable)> WithTrackingName(string name) => throw ExceptionUtilities.Unreachable; + public IIncrementalGeneratorNode<(IEnumerable, IEnumerable)> WithTrackingName(string name) => throw ExceptionUtilities.Unreachable(); - void IIncrementalGeneratorNode.RegisterOutput(IIncrementalGeneratorOutputNode output) => throw ExceptionUtilities.Unreachable; + void IIncrementalGeneratorNode.RegisterOutput(IIncrementalGeneratorOutputNode output) => throw ExceptionUtilities.Unreachable(); public void AppendOutputs(IncrementalExecutionContext context, CancellationToken cancellationToken) { diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs index 9954e7c6e0eee..c3b7b48cba0c1 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs @@ -28,7 +28,8 @@ internal enum SourceGeneratorSyntaxTreeInfo public partial struct SyntaxValueProvider { - private static readonly ObjectPool> s_stackPool = new ObjectPool>(static () => new Stack()); + private static readonly ObjectPool> s_stringStackPool = new ObjectPool>(static () => new Stack()); + private static readonly ObjectPool> s_nodeStackPool = new ObjectPool>(static () => new Stack()); /// /// Returns all syntax nodes of that match if that node has an attribute on it that @@ -170,87 +171,122 @@ private static ImmutableArray GetMatchingNodes( // Used to ensure that as we recurse through alias names to see if they could bind to attributeName that we // don't get into cycles. - var seenNames = s_stackPool.Allocate(); + var seenNames = s_stringStackPool.Allocate(); var results = ArrayBuilder.GetInstance(); var attributeTargets = ArrayBuilder.GetInstance(); try { - recurse(compilationUnit); + processCompilationUnit(compilationUnit); } finally { localAliases.Free(); seenNames.Clear(); - s_stackPool.Free(seenNames); + s_stringStackPool.Free(seenNames); attributeTargets.Free(); } results.RemoveDuplicates(); return results.ToImmutableAndFree(); - void recurse(SyntaxNode node) + void processCompilationUnit(SyntaxNode compilationUnit) { cancellationToken.ThrowIfCancellationRequested(); - if (node is ICompilationUnitSyntax) - { - syntaxHelper.AddAliases(node.Green, localAliases, global: false); + if (compilationUnit is ICompilationUnitSyntax) + syntaxHelper.AddAliases(compilationUnit.Green, localAliases, global: false); - recurseChildren(node); - } - else if (syntaxHelper.IsAnyNamespaceBlock(node)) - { - var localAliasCount = localAliases.Count; - syntaxHelper.AddAliases(node.Green, localAliases, global: false); + processCompilationOrNamespaceMembers(compilationUnit); + } - recurseChildren(node); + void processCompilationOrNamespaceMembers(SyntaxNode node) + { + cancellationToken.ThrowIfCancellationRequested(); - // after recursing into this namespace, dump any local aliases we added from this namespace decl itself. - localAliases.Count = localAliasCount; + foreach (var child in node.ChildNodesAndTokens()) + { + if (child.IsNode) + { + var childNode = child.AsNode()!; + if (syntaxHelper.IsAnyNamespaceBlock(childNode)) + processNamespaceBlock(childNode); + else + processMember(childNode); + } } - else if (syntaxHelper.IsAttributeList(node)) + } + + void processNamespaceBlock(SyntaxNode namespaceBlock) + { + cancellationToken.ThrowIfCancellationRequested(); + + var localAliasCount = localAliases.Count; + syntaxHelper.AddAliases(namespaceBlock.Green, localAliases, global: false); + + processCompilationOrNamespaceMembers(namespaceBlock); + + // after recursing into this namespace, dump any local aliases we added from this namespace decl itself. + localAliases.Count = localAliasCount; + } + + void processMember(SyntaxNode member) + { + cancellationToken.ThrowIfCancellationRequested(); + + // nodes can be arbitrarily deep. Use an explicit stack over recursion to prevent a stack-overflow. + var nodeStack = s_nodeStackPool.Allocate(); + nodeStack.Push(member); + + try { - foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node)) + while (nodeStack.Count > 0) { - // Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix. - // e.g. if there is [X] then we have to lookup with X and with XAttribute. - var simpleAttributeName = syntaxHelper.GetUnqualifiedIdentifierOfName(syntaxHelper.GetNameOfAttribute(attribute)); - if (matchesAttributeName(simpleAttributeName, withAttributeSuffix: false) || - matchesAttributeName(simpleAttributeName, withAttributeSuffix: true)) - { - attributeTargets.Clear(); - syntaxHelper.AddAttributeTargets(node, attributeTargets); + var node = nodeStack.Pop(); - foreach (var target in attributeTargets) + if (syntaxHelper.IsAttributeList(node)) + { + foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node)) { - if (predicate(target, cancellationToken)) - results.Add(target); + // Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix. + // e.g. if there is [X] then we have to lookup with X and with XAttribute. + var simpleAttributeName = syntaxHelper.GetUnqualifiedIdentifierOfName(syntaxHelper.GetNameOfAttribute(attribute)); + if (matchesAttributeName(simpleAttributeName, withAttributeSuffix: false) || + matchesAttributeName(simpleAttributeName, withAttributeSuffix: true)) + { + attributeTargets.Clear(); + syntaxHelper.AddAttributeTargets(node, attributeTargets); + + foreach (var target in attributeTargets) + { + if (predicate(target, cancellationToken)) + results.Add(target); + } + + break; + } } - return; + // attributes can't have attributes inside of them. so no need to recurse when we're done. + } + else + { + // For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot + // terminate the search anywhere as attributes may be found on things like local functions, and that + // means having to dive deep into statements and expressions. + foreach (var child in node.ChildNodesAndTokens().Reverse()) + { + if (child.IsNode) + nodeStack.Push(child.AsNode()!); + } } - } - // attributes can't have attributes inside of them. so no need to recurse when we're done. + } } - else + finally { - // For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot - // terminate the search anywhere as attributes may be found on things like local functions, and that - // means having to dive deep into statements and expressions. - recurseChildren(node); - } - - return; - - void recurseChildren(SyntaxNode node) - { - foreach (var child in node.ChildNodesAndTokens()) - { - if (child.IsNode) - recurse(child.AsNode()!); - } + nodeStack.Clear(); + s_nodeStackPool.Free(nodeStack); } } diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs index d5b83d44d8fbc..2bf7dd3ca6047 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs @@ -68,14 +68,9 @@ internal enum SymbolDisplayCompilerInternalOptions /// UsePlusForNestedTypes = 1 << 8, - /// - /// Includes the scoped keyword. - /// - IncludeScoped = 1 << 9, - /// /// Display `MyType@File.cs` instead of `MyType`. /// - IncludeContainingFileForFileTypes = 1 << 10, + IncludeContainingFileForFileTypes = 1 << 9, } } diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayLocalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayLocalOptions.cs index 61966366c51f3..b8c24c94dd67a 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayLocalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayLocalOptions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; namespace Microsoft.CodeAnalysis { @@ -31,8 +32,15 @@ public enum SymbolDisplayLocalOptions IncludeConstantValue = 1 << 1, /// - /// Includes the ref keyword for ref-locals. + /// Includes the ref keyword for ref-locals and the scoped keyword for scoped locals. + /// Replaced by . /// - IncludeRef = 1 << 2, + [EditorBrowsable(EditorBrowsableState.Never)] + IncludeRef = IncludeModifiers, + + /// + /// Includes the ref keyword for ref-locals and the scoped keyword for scoped locals. + /// + IncludeModifiers = 1 << 2, } } diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayParameterOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayParameterOptions.cs index b17abe5e66782..3b6712890a42e 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayParameterOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayParameterOptions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; namespace Microsoft.CodeAnalysis { @@ -30,9 +31,16 @@ public enum SymbolDisplayParameterOptions IncludeExtensionThis = 1 << 0, /// - /// Includes the params, ref, in, out, ByRef, ByVal keywords before parameters. + /// Includes the params, scoped, ref, in, out, ByRef, ByVal keywords before parameters. + /// Replaced by . /// - IncludeParamsRefOut = 1 << 1, + [EditorBrowsable(EditorBrowsableState.Never)] + IncludeParamsRefOut = IncludeModifiers, + + /// + /// Includes the params, scoped, ref, in, out, ByRef, ByVal keywords before parameters. + /// + IncludeModifiers = 1 << 1, /// /// Includes parameter types in symbol descriptions. diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs index c6434867f7a53..01913c92d4381 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs @@ -317,7 +317,7 @@ private ObsoleteAttributeData DecodeObsoleteAttribute() // Ideally we would use an abstract method, but that would require making the method visible to // public consumers who inherit from this class, which we don't want to do. // Therefore we just make it a 'private protected virtual' method instead. - private protected virtual bool IsStringProperty(string memberName) => throw ExceptionUtilities.Unreachable; + private protected virtual bool IsStringProperty(string memberName) => throw ExceptionUtilities.Unreachable(); /// /// Decode the arguments to DeprecatedAttribute. DeprecatedAttribute can have 3 or 4 arguments. diff --git a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.cs b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.cs index 7098769ac8a8f..dbb2c5e592407 100644 --- a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.cs +++ b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList.cs @@ -150,7 +150,7 @@ public sealed override string Language { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -158,18 +158,18 @@ public sealed override string KindText { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } public sealed override SyntaxNode GetStructure(SyntaxTrivia parentTrivia) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public sealed override SyntaxToken CreateSeparator(SyntaxNode element) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public sealed override bool IsTriviaWithEndOfLine() diff --git a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList`1.cs b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList`1.cs index 011dbe1683749..090efb272911c 100644 --- a/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList`1.cs +++ b/src/Compilers/Core/Portable/Syntax/InternalSyntax/SyntaxList`1.cs @@ -49,7 +49,7 @@ public TNode? this[int index] } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxList.cs b/src/Compilers/Core/Portable/Syntax/SyntaxList.cs index c0c1526b76a04..2073ef9448ed8 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxList.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxList.cs @@ -19,7 +19,7 @@ public override string Language { get { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -28,52 +28,52 @@ public override string Language protected internal override SyntaxNode ReplaceCore(IEnumerable? nodes = null, Func? computeReplacementNode = null, IEnumerable? tokens = null, Func? computeReplacementToken = null, IEnumerable? trivia = null, Func? computeReplacementTrivia = null) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode ReplaceNodeInListCore(SyntaxNode originalNode, IEnumerable replacementNodes) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode InsertNodesInListCore(SyntaxNode nodeInList, IEnumerable nodesToInsert, bool insertBefore) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode ReplaceTokenInListCore(SyntaxToken originalToken, IEnumerable newTokens) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode InsertTokensInListCore(SyntaxToken originalToken, IEnumerable newTokens, bool insertBefore) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode ReplaceTriviaInListCore(SyntaxTrivia originalTrivia, IEnumerable newTrivia) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode InsertTriviaInListCore(SyntaxTrivia originalTrivia, IEnumerable newTrivia, bool insertBefore) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode RemoveNodesCore(IEnumerable nodes, SyntaxRemoveOptions options) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected internal override SyntaxNode NormalizeWhitespaceCore(string indentation, string eol, bool elasticTrivia) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override bool IsEquivalentToCore(SyntaxNode node, bool topLevel = false) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } 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/Syntax/SyntaxNodeOrToken.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNodeOrToken.cs index 9b55b1f47556f..50b2f9822ae6d 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNodeOrToken.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNodeOrToken.cs @@ -958,7 +958,7 @@ internal static int GetFirstChildIndexSpanningPosition(ChildSyntaxList list, int } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public SyntaxNodeOrToken GetNextSibling() 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 74% rename from src/Compilers/Core/Portable/Debugging/SourceHashAlgorithms.cs rename to src/Compilers/Core/Portable/Text/SourceHashAlgorithms.cs index 32f2bfe281697..7ca4fd3067163 100644 --- a/src/Compilers/Core/Portable/Debugging/SourceHashAlgorithms.cs +++ b/src/Compilers/Core/Portable/Text/SourceHashAlgorithms.cs @@ -2,19 +2,25 @@ // 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; + + /// + /// Defines a source hash algorithm constant we can re-use when creating source texts for open documents. + /// This ensures that both LSP and documents opened as a text buffer are created with the same checksum algorithm + /// so that we can compare their contents using checksums later on. + /// + public const SourceHashAlgorithm OpenDocumentChecksumAlgorithm = Default; + 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/Rebuild/Microsoft.CodeAnalysis.Rebuild.csproj b/src/Compilers/Core/Rebuild/Microsoft.CodeAnalysis.Rebuild.csproj index 0f5d4be204a2c..1910d8861e1af 100644 --- a/src/Compilers/Core/Rebuild/Microsoft.CodeAnalysis.Rebuild.csproj +++ b/src/Compilers/Core/Rebuild/Microsoft.CodeAnalysis.Rebuild.csproj @@ -9,6 +9,7 @@ AnyCPU enable true + false diff --git a/src/Compilers/Core/Rebuild/RebuildSourceReferenceResolver.cs b/src/Compilers/Core/Rebuild/RebuildSourceReferenceResolver.cs index d4266d342b855..5c281ef6e30b3 100644 --- a/src/Compilers/Core/Rebuild/RebuildSourceReferenceResolver.cs +++ b/src/Compilers/Core/Rebuild/RebuildSourceReferenceResolver.cs @@ -61,9 +61,9 @@ private RebuildSourceReferenceResolver() return null; } - public override Stream OpenRead(string resolvedPath) => throw ExceptionUtilities.Unreachable; + public override Stream OpenRead(string resolvedPath) => throw ExceptionUtilities.Unreachable(); - public override string? ResolveReference(string path, string? baseFilePath) => throw ExceptionUtilities.Unreachable; + public override string? ResolveReference(string path, string? baseFilePath) => throw ExceptionUtilities.Unreachable(); } } 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/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj b/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj index c01139fd1e963..11eb83bbe224b 100644 --- a/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj +++ b/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.Rebuild.UnitTests true - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/Core/RebuildTest/RoundTripUtil.cs b/src/Compilers/Core/RebuildTest/RoundTripUtil.cs index e2cef09b916f2..49dd006e88a68 100644 --- a/src/Compilers/Core/RebuildTest/RoundTripUtil.cs +++ b/src/Compilers/Core/RebuildTest/RoundTripUtil.cs @@ -40,7 +40,7 @@ public static void VerifyRoundTrip( var portablePdbReader = pdbStream is not null ? MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader() : null; Assert.True(embeddedPdbReader == null ^ portablePdbReader == null); - var pdbReader = embeddedPdbReader ?? portablePdbReader ?? throw ExceptionUtilities.Unreachable; + var pdbReader = embeddedPdbReader ?? portablePdbReader ?? throw ExceptionUtilities.Unreachable(); var factory = LoggerFactory.Create(configure => { }); var logger = factory.CreateLogger("RoundTripVerification"); var optionsReader = new CompilationOptionsReader(logger, pdbReader, peReader); diff --git a/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj b/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj index c9837aee3503a..31ece03c7f4d1 100644 --- a/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj +++ b/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj @@ -5,6 +5,7 @@ Library Roslyn.Compilers.Extension net472 + false true diff --git a/src/Compilers/Server/VBCSCompiler/AnalyzerConsistencyChecker.cs b/src/Compilers/Server/VBCSCompiler/AnalyzerConsistencyChecker.cs index ba0b1f6507569..e9f5c24b08600 100644 --- a/src/Compilers/Server/VBCSCompiler/AnalyzerConsistencyChecker.cs +++ b/src/Compilers/Server/VBCSCompiler/AnalyzerConsistencyChecker.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if NETFRAMEWORK + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -90,13 +92,32 @@ private static bool CheckCore( } // Third, check that the MVIDs of the files on disk match the MVIDs of the loaded assemblies. + var comparer = PathUtilities.Comparer; + var compilerDirectory = Path.GetDirectoryName(typeof(AnalyzerConsistencyChecker).Assembly.CodeBase); + for (int i = 0; i < resolvedPaths.Count; i++) { var resolvedPath = resolvedPaths[i]; var loadedAssembly = loadedAssemblies[i]; + + // When an assembly is loaded from the GAC then the load result would be the same if + // this ran on command line compiler. So there is no consistency issue here, this + // is just runtime rules expressing themselves. + if (loadedAssembly.GlobalAssemblyCache) + { + continue; + } + + // When an assembly is loaded from the compiler directory then this means it's assembly + // binding redirects taking over. For example it's moving from an older version of System.Memory + // to the one shipping in the compiler. This is not a consistency issue. + if (PathUtilities.Comparer.Equals(compilerDirectory, Path.GetDirectoryName(loadedAssembly.CodeBase))) + { + continue; + } + var resolvedPathMvid = AssemblyUtilities.ReadMvid(resolvedPath); var loadedAssemblyMvid = loadedAssembly.ManifestModule.ModuleVersionId; - if (resolvedPathMvid != loadedAssemblyMvid) { var message = $"analyzer assembly '{resolvedPath}' has MVID '{resolvedPathMvid}' but loaded assembly '{loadedAssembly.FullName}' has MVID '{loadedAssemblyMvid}'"; @@ -109,3 +130,5 @@ private static bool CheckCore( } } } + +#endif diff --git a/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj b/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj index 2f0adbf0c28fc..30d134eac4064 100644 --- a/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj +++ b/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj @@ -5,6 +5,7 @@ Exe net6.0;net472 false + true diff --git a/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs b/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs index 9513b304ff182..296e8d6ff91e6 100644 --- a/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs +++ b/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs @@ -74,11 +74,6 @@ internal CompilerServerHost(string clientDirectory, string sdkDirectory, ICompil Logger = logger; } - private bool CheckAnalyzers(string baseDirectory, ImmutableArray analyzers, [NotNullWhen(false)] out List? errorMessages) - { - return AnalyzerConsistencyChecker.Check(baseDirectory, analyzers, AnalyzerAssemblyLoader, Logger, out errorMessages); - } - public bool TryCreateCompiler(in RunRequest request, BuildPaths buildPaths, [NotNullWhen(true)] out CommonCompiler? compiler) { switch (request.Language) @@ -141,16 +136,18 @@ public BuildResponse RunCompilation(in RunRequest request, CancellationToken can return new RejectedBuildResponse(message); } - bool utf8output = compiler.Arguments.Utf8Output; - if (!CheckAnalyzers(request.WorkingDirectory, compiler.Arguments.AnalyzerReferences, out List? errorMessages)) +#if NETFRAMEWORK + if (!AnalyzerConsistencyChecker.Check(request.WorkingDirectory, compiler.Arguments.AnalyzerReferences, AnalyzerAssemblyLoader, Logger, out List errorMessages)) { Logger.Log($"Rejected: {request.RequestId}: for analyzer load issues {string.Join(";", errorMessages)}"); return new AnalyzerInconsistencyBuildResponse(new ReadOnlyCollection(errorMessages)); } +#endif Logger.Log($"Begin {request.RequestId} {request.Language} compiler run"); try { + bool utf8output = compiler.Arguments.Utf8Output; TextWriter output = new StringWriter(CultureInfo.InvariantCulture); int returnCode = compiler.Run(output, cancellationToken); var outputString = output.ToString(); diff --git a/src/Compilers/Server/VBCSCompiler/arm64/VBCSCompiler-arm64.csproj b/src/Compilers/Server/VBCSCompiler/arm64/VBCSCompiler-arm64.csproj index 684dfed0af580..c12e08c979e86 100644 --- a/src/Compilers/Server/VBCSCompiler/arm64/VBCSCompiler-arm64.csproj +++ b/src/Compilers/Server/VBCSCompiler/arm64/VBCSCompiler-arm64.csproj @@ -9,6 +9,7 @@ ARM64 net472 true + true diff --git a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs index b7014a8ebb516..21b42d7b517df 100644 --- a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs @@ -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. +#if NETFRAMEWORK #nullable disable using System; @@ -14,6 +15,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; @@ -41,6 +44,39 @@ public AnalyzerConsistencyCheckerTests(ITestOutputHelper testOutputHelper, Assem TestFixture = testFixture; } + private TempFile CreateNetStandardDll(TempDirectory directory, string assemblyName, string version, ImmutableArray publicKey, string? extraSource = null) + { + var source = $$""" + using System; + using System.Reflection; + + [assembly: AssemblyVersion("{{version}}")] + [assembly: AssemblyFileVersion("{{version}}")] + """; + + var sources = extraSource is null + ? new[] { CSharpTestSource.Parse(source) } + : new[] { CSharpTestSource.Parse(source), CSharpTestSource.Parse(extraSource) }; + + var options = new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + warningLevel: Diagnostic.MaxWarningLevel, + cryptoPublicKey: publicKey, + deterministic: true, + publicSign: true); + + var comp = CSharpCompilation.Create( + assemblyName, + sources, + references: NetStandard20.All, + options: options); + + var file = directory.CreateFile($"{assemblyName}.dll"); + var emitResult = comp.Emit(file.Path); + Assert.Empty(emitResult.Diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error)); + return file; + } + [Fact] public void MissingReference() { @@ -71,25 +107,66 @@ public void DifferingMvids() { var directory = Temp.CreateDirectory(); - // Load Beta.dll from the future Alpha.dll path to prime the assembly loader - var alphaDll = directory.CopyFile(TestFixture.Beta.Path, name: "Alpha.dll"); + var key = NetStandard20.netstandard.GetAssemblyIdentity().PublicKey; + var mvidAlpha1 = CreateNetStandardDll(directory.CreateDirectory("mvid1"), "MvidAlpha", "1.0.0.0", key, "class C { }"); + var mvidAlpha2 = CreateNetStandardDll(directory.CreateDirectory("mvid2"), "MvidAlpha", "1.0.0.0", key, "class D { }"); + + // Can't use InMemoryAssemblyLoader because that uses the None context which fakes paths + // to always be the currently executing application. That makes it look like everything + // is in the same directory + var assemblyLoader = new DefaultAnalyzerAssemblyLoader(); + var analyzerReferences = ImmutableArray.Create( + new CommandLineAnalyzerReference(mvidAlpha1.Path), + new CommandLineAnalyzerReference(mvidAlpha2.Path)); - var assemblyLoader = new InMemoryAssemblyLoader(); - var betaAssembly = assemblyLoader.LoadFromPath(alphaDll.Path); + var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, assemblyLoader, Logger); + Assert.False(result); + } - // now overwrite the {directory}/Alpha.dll file with the content from our Alpha.dll test resource - alphaDll.CopyContentFrom(TestFixture.Alpha.Path); - directory.CopyFile(TestFixture.Gamma.Path); - directory.CopyFile(TestFixture.Delta1.Path); + /// + /// A differing MVID is okay when it's loading a DLL from the compiler directory. That is + /// considered an exchange type. For example if an analyzer has a reference to System.Memory + /// it will always load the copy the compiler used and that is not a consistency issue. + /// + [Fact] + [WorkItem(64826, "https://github.com/dotnet/roslyn/issues/64826")] + public void LoadingLibraryFromCompiler() + { + var directory = Temp.CreateDirectory(); + var dllFile = CreateNetStandardDll(directory, "System.Memory", "2.0.0.0", NetStandard20.netstandard.GetAssemblyIdentity().PublicKey); + // This test must use the DefaultAnalyzerAssemblyLoader as we want assembly binding redirects + // to take affect here. + var assemblyLoader = new DefaultAnalyzerAssemblyLoader(); var analyzerReferences = ImmutableArray.Create( - new CommandLineAnalyzerReference("Alpha.dll"), - new CommandLineAnalyzerReference("Gamma.dll"), - new CommandLineAnalyzerReference("Delta.dll")); + new CommandLineAnalyzerReference("System.Memory.dll")); var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, assemblyLoader, Logger); - Assert.False(result); + Assert.True(result); + } + + /// + /// A differing MVID is okay when it's loading a DLL from the GAC. There is no reason that + /// falling back to csc would change the load result. + /// + [Fact] + [WorkItem(64826, "https://github.com/dotnet/roslyn/issues/64826")] + public void LoadingLibraryFromGAC() + { + var directory = Temp.CreateDirectory(); + var dllFile = directory.CreateFile("System.Core.dll"); + dllFile.WriteAllBytes(NetStandard20.Resources.SystemCore); + + // This test must use the DefaultAnalyzerAssemblyLoader as we want assembly binding redirects + // to take affect here. + var assemblyLoader = new DefaultAnalyzerAssemblyLoader(); + var analyzerReferences = ImmutableArray.Create( + new CommandLineAnalyzerReference("System.Core.dll")); + + var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, assemblyLoader, Logger); + + Assert.True(result); } [Fact] @@ -107,22 +184,15 @@ public void AssemblyLoadException() } [Fact] - public void NetstandardIgnored() + public void LoadingSimpleLibrary() { var directory = Temp.CreateDirectory(); - const string name = "netstandardRef"; - var comp = CSharpCompilation.Create( - name, - new[] { SyntaxFactory.ParseSyntaxTree(@"class C {}") }, - references: new MetadataReference[] { NetStandard20.netstandard }, - options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, warningLevel: Diagnostic.MaxWarningLevel)); - var compFile = directory.CreateFile(name); - comp.Emit(compFile.Path); + var key = NetStandard20.netstandard.GetAssemblyIdentity().PublicKey; + var compFile = CreateNetStandardDll(directory, "netstandardRef", "1.0.0.0", key); + var analyzerReferences = ImmutableArray.Create(new CommandLineAnalyzerReference(compFile.Path)); - var analyzerReferences = ImmutableArray.Create(new CommandLineAnalyzerReference(name)); - - var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, new InMemoryAssemblyLoader(), Logger); + var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, new DefaultAnalyzerAssemblyLoader(), Logger); Assert.True(result); } @@ -150,3 +220,4 @@ public Assembly LoadFromPath(string fullPath) } } } +#endif diff --git a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index 084293f127f64..5ca78be0ecca7 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -449,8 +449,11 @@ public void CompilerBinariesAreNotX86() { var basePath = Path.GetDirectoryName(typeof(CompilerServerUnitTests).Assembly.Location); var compilerServerExecutable = Path.Combine(basePath, "VBCSCompiler.exe"); +#pragma warning disable SYSLIB0037 + // warning SYSLIB0037: 'AssemblyName.ProcessorArchitecture' is obsolete: 'AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete and not supported.' Assert.NotEqual(ProcessorArchitecture.X86, AssemblyName.GetAssemblyName(compilerServerExecutable).ProcessorArchitecture); +#pragma warning restore SYSLIB0037 } /// diff --git a/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj b/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj index 8ee6e9160de67..eda6facd1be0e 100644 --- a/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj +++ b/src/Compilers/Server/VBCSCompilerTests/VBCSCompiler.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.CompilerServer.UnitTests true - net6.0;net472 + net7.0;net472 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/IOperation/IOperation/IOperationTests_IAttributeOperation.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.vb new file mode 100644 index 0000000000000..26f7faa4ea2c8 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IAttributeOperation.vb @@ -0,0 +1,748 @@ +' 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.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + + Public Sub TestCallerInfoImplicitCall() + Dim source = Optional lineNumber As Integer = -1) + Console.WriteLine(lineNumber) + End Sub +End Class + +'BIND:"A" +Class Test +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Class() + Dim source = Optional callerName As String = "") + Console.WriteLine(callerName) + End Sub +End Class + +'BIND:"A" +Class Test +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Method() + Dim source = Optional callerName As String = "") + Console.WriteLine(callerName) + End Sub +End Class + +Class Test + 'BIND:"A" + Public Sub M() + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestCallerMemberName_Parameter() + Dim source = Optional callerName As String = "") + End Sub +End Class + +Class Test + Public Sub M( x As String)'BIND:"My" + End Sub +End Class +]]>.Value + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestNonExistingAttribute() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithoutArguments() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgument() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgument_IncorrectTypePassed() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithExplicitArgumentOptionalParameter() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestAttributeWithOptionalParameterNotPassed(withParentheses As Boolean) + Dim attribute = If(withParentheses, "My()", "My") + Dim attributeListSyntax = "<" + attribute + ">" + "'BIND:""My""" + Dim source = .Value + attributeListSyntax + .Value + + Dim expectedOperationTree = +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '<%= attribute %>') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor([value As System.String = ""])) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: '<%= attribute %>') + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'My') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsImplicit) (Syntax: 'My') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +.Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub TestConversion() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub BadAttributeParameterType() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub BadAttributeParameterType2() + Dim source = 'BIND:"My" +Class C +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithExplicitNullArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithDefaultNullArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeWithTypeOfArgument() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + Public Sub New(Optional x As Type = Nothing) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub InvalidValue() + Dim source = 'BIND:"A" +Class A + Inherits CodeAccessSecurityAttribute + Public Sub New(Optional x As SecurityAction = 0) + MyBase.New(x) + End Sub +End Class +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"A" + ~ +BC30610: Class 'A' must either be declared 'MustInherit' or override the following inherited 'MustOverride' member(s): + SecurityAttribute: Public MustOverride Overloads Function CreatePermission() As IPermission. +Class A + ~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub InvalidAttributeParameterType() + Dim source = 'BIND:"My" +Class MyAttribute + Inherits Attribute + + Public Sub New(ParamArray x As Integer()(,)) + End Sub +End Class + +]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = 'BIND:"My" + ~~ +]]>.Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + + + Public Sub AssemblyAndModuleAttributeTargets(attributeTarget As String) + Dim source = $" +Imports System + +<{attributeTarget}: CLSCompliant(True)>'BIND:""{attributeTarget}: CLSCompliant(True)"" +" + Dim syntax As String + If attributeTarget = "Assembly" Then + syntax = "Assembly: C ... liant(True)" + ElseIf attributeTarget = "Module" Then + syntax = "Module: CLS ... liant(True)" + Else + Throw TestExceptionUtilities.UnexpectedValue(attributeTarget) + End If + + Dim expectedOperationTree = $" +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: '{syntax}') + IObjectCreationOperation (Constructor: Sub System.CLSCompliantAttribute..ctor(isCompliant As System.Boolean)) (OperationKind.ObjectCreation, Type: System.CLSCompliantAttribute, IsImplicit) (Syntax: '{syntax}') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: isCompliant) (OperationKind.Argument, Type: null) (Syntax: 'True') + ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'True') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub ReturnAttributeTarget() + Dim source = String 'BIND:"My(10)" + Return Nothing + End Function +End Class +]]>.Value + + Dim expectedOperationTree = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor(x As System.Int32)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AttributeOnEnumMember() + Dim source = 'BIND:"My(10)" + A +End Enum +]]>.Value + + Dim expectedOperationTree = " +IAttributeOperation (OperationKind.Attribute, Type: null) (Syntax: 'My(10)') + IObjectCreationOperation (Constructor: Sub MyAttribute..ctor(x As System.Int32)) (OperationKind.ObjectCreation, Type: MyAttribute, IsImplicit) (Syntax: 'My(10)') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +" + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb index 24f969a680083..edc7d1cf8d829 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IFieldReferenceExpression.vb @@ -27,11 +27,17 @@ Class C End Class]]>.Value Dim expectedOperationTree = .Value Dim expectedDiagnostics = String.Empty diff --git a/src/Compilers/VisualBasic/Test/IOperation/Roslyn.Compilers.VisualBasic.IOperation.UnitTests.vbproj b/src/Compilers/VisualBasic/Test/IOperation/Roslyn.Compilers.VisualBasic.IOperation.UnitTests.vbproj index 9a9573d6dd6b9..a575e2710ed63 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/Roslyn.Compilers.VisualBasic.IOperation.UnitTests.vbproj +++ b/src/Compilers/VisualBasic/Test/IOperation/Roslyn.Compilers.VisualBasic.IOperation.UnitTests.vbproj @@ -3,7 +3,7 @@ Library - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingCollectionInitializerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingCollectionInitializerTests.vb index 7a3c32a664547..1df82543aa036 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingCollectionInitializerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingCollectionInitializerTests.vb @@ -1849,7 +1849,7 @@ End Class Assert.Equal(2, symbolInfo.CandidateSymbols.Length) Assert.Equal({"Sub X.Add(x As System.Collections.Generic.List(Of System.Byte))", "Sub X.Add(x As X)"}, - symbolInfo.CandidateSymbols.Select(Function(s) s.ToTestDisplayString()).Order().ToArray()) + Roslyn.Utilities.EnumerableExtensions.Order(symbolInfo.CandidateSymbols.Select(Function(s) s.ToTestDisplayString())).ToArray()) End Sub 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/Semantic/Compilation/SemanticModelAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb index 810ed5c4ce290..07e948521c7d6 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb @@ -721,6 +721,7 @@ End Class Dim success = semanticModel.TryGetSpeculativeSemanticModel(position1, initializer, speculativeModel) Assert.True(success) Assert.NotNull(speculativeModel) + Assert.False(speculativeModel.IgnoresAccessibility) Dim typeInfo = speculativeModel.GetTypeInfo(expression) Assert.Equal("System.Int16", typeInfo.Type.ToTestDisplayString()) @@ -728,6 +729,10 @@ End Class Dim constantInfo = speculativeModel.GetConstantValue(expression) Assert.True(constantInfo.HasValue, "must be a constant") Assert.Equal(CType(0, System.Int16), constantInfo.Value) + + semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility:=True) + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(position1, initializer, speculativeModel)) + Assert.True(speculativeModel.IgnoresAccessibility) End Sub @@ -1082,12 +1087,18 @@ End Module Dim success = semanticModel.TryGetSpeculativeSemanticModel(position1, speculatedRangeArgument, speculativeModel) Assert.True(success) Assert.NotNull(speculativeModel) + Assert.False(speculativeModel.IgnoresAccessibility) Dim upperBound = speculatedRangeArgument.UpperBound Dim symbolInfo = speculativeModel.GetSymbolInfo(upperBound) Assert.NotNull(symbolInfo.Symbol) Assert.Equal(SymbolKind.Method, symbolInfo.Symbol.Kind) Assert.Equal("NewMethod", symbolInfo.Symbol.Name) + + semanticModel = compilation.GetSemanticModel(tree, ignoreAccessibility:=True) + Assert.True(semanticModel.TryGetSpeculativeSemanticModel(position1, speculatedRangeArgument, speculativeModel)) + Assert.NotNull(speculativeModel) + Assert.True(speculativeModel.IgnoresAccessibility) End Sub @@ -1710,6 +1721,7 @@ End Class Dim success = model.TryGetSpeculativeSemanticModel(position, speculatedTypeSyntax, speculativeModel, bindingOption) Assert.True(success) Assert.NotNull(speculativeModel) + Assert.False(speculativeModel.IgnoresAccessibility) Assert.True(speculativeModel.IsSpeculativeSemanticModel) Assert.Equal(model, speculativeModel.ParentModel) @@ -1816,6 +1828,13 @@ End Namespace Dim implementsClause = typeBlock.Implements(0) TestGetSpeculativeSemanticModelForTypeSyntax_Common(model, implementsClause.Types.First.Position, speculatedTypeExpression, SpeculativeBindingOption.BindAsExpression, SymbolKind.NamedType, "N.I") + + model = compilation.GetSemanticModel(tree, ignoreAccessibility:=True) + Dim speculativeModel As SemanticModel = Nothing + Dim success = model.TryGetSpeculativeSemanticModel(inheritsClause.Types.First.Position, speculatedTypeExpression, speculativeModel, SpeculativeBindingOption.BindAsExpression) + Assert.True(success) + Assert.NotNull(speculativeModel) + Assert.True(speculativeModel.IgnoresAccessibility) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb index bb3d03e3ad70f..ea1f007c6443d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb @@ -1678,7 +1678,7 @@ End Namespace Assert.Equal(Analyzer.Descriptor.Id, diagnostic.Id) Assert.Equal(LocationKind.ExternalFile, diagnostic.Location.Kind) Dim location = DirectCast(diagnostic.Location, ExternalFileLocation) - Assert.Equal(additionalFile.Path, location.FilePath) + Assert.Equal(additionalFile.Path, location.GetLineSpan().Path) Assert.Equal(expectedDiagnosticSpan, location.SourceSpan) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.vbproj b/src/Compilers/VisualBasic/Test/Semantic/Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.vbproj index 6b3c5cba79e4b..da40b2805f75c 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.vbproj @@ -3,7 +3,7 @@ Library - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index e2b1cdd3c6310..0062bb9fd803d 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -5324,7 +5324,7 @@ ref struct S End Sub ''' - ''' IParameterSymbol.IsRefScoped and IsValueScoped are ignored in VisualBasic.SymbolDisplayVisitor. + ''' IParameterSymbol.ScopedKind is ignored in VisualBasic.SymbolDisplayVisitor. ''' @@ -5334,19 +5334,19 @@ ref struct S "ref struct R { } class Program { - static void F(scoped R r1, scoped ref R r3) { } + static void F(scoped R r1, scoped ref R r3, scoped out R r4) { } }" Dim comp = CreateCSharpCompilation(GetUniqueName(), source, parseOptions:=New CSharp.CSharpParseOptions(CSharp.LanguageVersion.Preview)) comp.VerifyDiagnostics() Dim method = comp.GlobalNamespace.GetTypeMembers("Program").Single().GetMembers("F").Single() - Dim format = SymbolDisplayFormat.TestFormat + Dim format = SymbolDisplayFormat.TestFormat.WithParameterOptions(SymbolDisplayParameterOptions.IncludeType Or SymbolDisplayParameterOptions.IncludeName) If includeScoped Then - format = format.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped) + format = format.AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut) End If Verify(SymbolDisplay.ToDisplayParts(method, format), - "Sub Program.F(r1 As R, r3 As R)", + "Sub Program.F(r1 As R, r3 As R, r4 As R)", SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.ClassName, @@ -5365,11 +5365,18 @@ class Program SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, SymbolDisplayPartKind.Punctuation) End Sub ''' - ''' ILocalSymbol.IsRefScoped and IsValueScoped are ignored in VisualBasic.SymbolDisplayVisitor. + ''' ILocalSymbol.ScopedKind is ignored in VisualBasic.SymbolDisplayVisitor. ''' @@ -5392,9 +5399,9 @@ class Program Dim decls = tree.GetRoot().DescendantNodes().OfType(Of Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclaratorSyntax)().ToArray() Dim locals = decls.Select(Function(d) model.GetDeclaredSymbol(d)).ToArray() - Dim format = SymbolDisplayFormat.TestFormat.AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef) + Dim format = SymbolDisplayFormat.TestFormat.WithLocalOptions(SymbolDisplayLocalOptions.IncludeType) If includeScoped Then - format = format.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped) + format = format.AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef) End If Verify(SymbolDisplay.ToDisplayParts(locals(0), format), diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/AttributeTests_UnmanagedCallersOnly.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/AttributeTests_UnmanagedCallersOnly.vb index a2890824bbe67..5c16436a367d7 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/AttributeTests_UnmanagedCallersOnly.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/AttributeTests_UnmanagedCallersOnly.vb @@ -13,7 +13,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Private ReadOnly _parseOptions As CSharp.CSharpParseOptions = CSharp.CSharpParseOptions.Default.WithLanguageVersion(CSharp.LanguageVersion.Default) Private ReadOnly _csharpCompOptions As CSharp.CSharpCompilationOptions = New CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe:=True) - Private ReadOnly _csharpReferences As ImmutableArray(Of MetadataReference) = TargetFrameworkUtil.GetReferences(TargetFramework.NetCoreApp).Add(NetCoreApp.SystemRuntimeInteropServices) + Private ReadOnly _csharpReferences As ImmutableArray(Of MetadataReference) = TargetFrameworkUtil.GetReferences(TargetFramework.Net50).Add(NetCoreApp.SystemRuntimeInteropServices) Private ReadOnly UnmanagedCallersOnlyAttributeIl As String = - Dim comp = CreateCompilation(source, references:={NetCoreApp.SystemRuntimeInteropServices}, targetFramework:=TargetFramework.NetCoreApp) + Dim comp = CreateCompilation(source, references:={NetCoreApp.SystemRuntimeInteropServices}, targetFramework:=TargetFramework.Net50) comp.AssertTheseDiagnostics( - Dim comp = CreateCompilation(source, references:={NetCoreApp.SystemRuntimeInteropServices, reference}, targetFramework:=TargetFramework.NetCoreApp) + Dim comp = CreateCompilation(source, references:={NetCoreApp.SystemRuntimeInteropServices, reference}, targetFramework:=TargetFramework.Net50) comp.AssertTheseDiagnostics( Public Sub NoPia_06() - Dim attributesRef = GetCSharpCompilation(NoPiaAttributes).EmitToImageReference() + Dim attributesRef = GetCSharpCompilation(NoPiaAttributes, targetFramework:=TargetFramework.Net50).EmitToImageReference() Dim csSource = " @@ -1932,7 +1932,7 @@ public interface ITest33 } " - Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}).EmitToImageReference(embedInteropTypes:=True) + Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}, targetFramework:=TargetFramework.Net50).EmitToImageReference(embedInteropTypes:=True) Dim source1 = @@ -1956,7 +1956,7 @@ BC31558: Nested type 'ITest33.I1' cannot be embedded. Public Sub NoPia_07() - Dim attributesRef = GetCSharpCompilation(NoPiaAttributes).EmitToImageReference() + Dim attributesRef = GetCSharpCompilation(NoPiaAttributes, targetFramework:=TargetFramework.Net50).EmitToImageReference() Dim csSource = " @@ -1975,7 +1975,7 @@ public interface ITest33 } " - Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}).EmitToImageReference(embedInteropTypes:=True) + Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}, targetFramework:=TargetFramework.Net50).EmitToImageReference(embedInteropTypes:=True) Dim source1 = @@ -2001,7 +2001,7 @@ BC37307: Type 'ITest33' cannot be embedded because it has a non-abstract member. Public Sub NoPia_10() - Dim attributesRef = GetCSharpCompilation(NoPiaAttributes).EmitToImageReference() + Dim attributesRef = GetCSharpCompilation(NoPiaAttributes, targetFramework:=TargetFramework.Net50).EmitToImageReference() Dim csSource = " @@ -2027,7 +2027,7 @@ public interface ITest44 : ITest33 } " - Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}).EmitToImageReference(embedInteropTypes:=True) + Dim csCompilation = GetCSharpCompilation(csSource, {attributesRef}, targetFramework:=TargetFramework.Net50).EmitToImageReference(embedInteropTypes:=True) Dim source1 = 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/Microsoft.CodeAnalysis.VisualBasic.Syntax.UnitTests.vbproj b/src/Compilers/VisualBasic/Test/Syntax/Microsoft.CodeAnalysis.VisualBasic.Syntax.UnitTests.vbproj index b82051c771659..cd4be3f8ebbe3 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Microsoft.CodeAnalysis.VisualBasic.Syntax.UnitTests.vbproj +++ b/src/Compilers/VisualBasic/Test/Syntax/Microsoft.CodeAnalysis.VisualBasic.Syntax.UnitTests.vbproj @@ -3,7 +3,7 @@ Library - net6.0;net472 + net7.0;net472 diff --git a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb index 2bd43c040f45c..95b80b31dc5a5 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxTreeTests.vb @@ -10,12 +10,20 @@ Imports Roslyn.Test.Utilities.TestHelpers Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class VisualBasicSyntaxTreeTests + + Public Sub Create() + Dim root = SyntaxFactory.ParseCompilationUnit("") + + Dim tree = VisualBasicSyntaxTree.Create(root) + Assert.Equal(SourceHashAlgorithm.Sha1, tree.GetText().ChecksumAlgorithm) + End Sub + ' Diagnostic options on syntax trees are now obsolete -#Disable warning BC40000 +#Disable Warning BC40000 - Public Sub CreateTreeWithDiagnosticOptions() + 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 @@ -95,7 +103,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Same(map, newTree.DiagnosticOptions) Assert.NotEqual(tree, newTree) End Sub -#Enable warning BC40000 +#Enable Warning BC40000 Public Sub WithRootAndOptions_ParsedTree() @@ -114,7 +122,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 +134,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 +162,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 +171,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/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj b/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj index f8f2ea41243a1..daf62e0f9e1db 100644 --- a/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj +++ b/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj @@ -5,6 +5,7 @@ Exe net472;net6.0 false + true diff --git a/src/Compilers/VisualBasic/vbc/arm64/vbc-arm64.csproj b/src/Compilers/VisualBasic/vbc/arm64/vbc-arm64.csproj index 685e25eb43ca2..754c6f4568695 100644 --- a/src/Compilers/VisualBasic/vbc/arm64/vbc-arm64.csproj +++ b/src/Compilers/VisualBasic/vbc/arm64/vbc-arm64.csproj @@ -9,6 +9,7 @@ ARM64 vbc true + true diff --git a/src/Dependencies/CodeAnalysis.Debugging/CustomDebugInfoReader.cs b/src/Dependencies/CodeAnalysis.Debugging/CustomDebugInfoReader.cs index 306101e3a025f..a6b5aab8dd43e 100644 --- a/src/Dependencies/CodeAnalysis.Debugging/CustomDebugInfoReader.cs +++ b/src/Dependencies/CodeAnalysis.Debugging/CustomDebugInfoReader.cs @@ -202,16 +202,20 @@ public static ImmutableArray DecodeStateMachineHo /// Indicates that this method is the iterator state machine for the method named in the record. /// /// - /// Appears when are iterator methods. + /// Appears on kick-off methods of a state machine. /// Exposed for . + /// + /// Encodes NULL-terminated UTF16 name of the state machine type. + /// The ending NULL character might not be present if the PDB was generated by an older compiler. /// + /// Bad data. public static string DecodeForwardIteratorRecord(ImmutableArray bytes) { var offset = 0; var pooled = PooledStringBuilder.GetInstance(); var builder = pooled.Builder; - while (true) + while (offset < bytes.Length) { var ch = (char)ReadInt16(bytes, ref offset); if (ch == 0) diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 14b8ea2c5527a..35a61805d82a5 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -27,6 +27,7 @@ using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion { @@ -64,17 +65,15 @@ public AutomaticLineEnderCommandHandler( protected override void NextAction(IEditorOperations editorOperation, Action nextAction) => editorOperation.InsertNewLine(); - protected override bool TreatAsReturn(Document document, int caretPosition, CancellationToken cancellationToken) + protected override bool TreatAsReturn(ParsedDocument document, int caretPosition, CancellationToken cancellationToken) { - var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); - - var endToken = root.FindToken(caretPosition); + var endToken = document.Root.FindToken(caretPosition); if (endToken.IsMissing) { return false; } - var tokenToLeft = root.FindTokenOnLeftOfPosition(caretPosition); + var tokenToLeft = document.Root.FindTokenOnLeftOfPosition(caretPosition); var startToken = endToken.GetPreviousToken(); // case 1: @@ -98,25 +97,23 @@ protected override bool TreatAsReturn(Document document, int caretPosition, Canc return afterOpenBrace; } - protected override Document FormatAndApplyBasedOnEndToken(Document document, int position, SyntaxFormattingOptions options, CancellationToken cancellationToken) + protected override IList FormatBasedOnEndToken(ParsedDocument document, int position, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); + var root = document.Root; var endToken = root.FindToken(position); var span = GetFormattedTextSpan(root, endToken); if (span == null) { - return document; + return SpecializedCollections.EmptyList(); } - var formatter = document.GetRequiredLanguageService(); - var changes = formatter.GetFormattingResult( + var formatter = document.LanguageServices.GetRequiredService(); + return formatter.GetFormattingResult( root, SpecializedCollections.SingletonCollection(CommonFormattingHelpers.GetFormattingSpan(root, span.Value)), options, rules: null, cancellationToken).GetTextChanges(cancellationToken); - - return document.ApplyTextChanges(changes, cancellationToken); } private static TextSpan? GetFormattedTextSpan(SyntaxNode root, SyntaxToken endToken) @@ -143,12 +140,11 @@ protected override Document FormatAndApplyBasedOnEndToken(Document document, int #region SemicolonAppending - protected override string? GetEndingString(Document document, int position, CancellationToken cancellationToken) + protected override string? GetEndingString(ParsedDocument document, int position) { - // prepare expansive information from document - var tree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var root = tree.GetRoot(cancellationToken); - var text = tree.GetText(cancellationToken); + var root = document.Root; + var text = document.Text; + var tree = document.SyntaxTree; // Go through the set of owning nodes in leaf to root chain. foreach (var owningNode in GetOwningNodes(root, position)) @@ -320,14 +316,13 @@ or UsingDirectiveSyntax protected override void ModifySelectedNode( AutomaticLineEnderCommandArgs args, - Document document, + ParsedDocument document, SyntaxNode selectedNode, bool addBrace, int caretPosition, CancellationToken cancellationToken) { - var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); - var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.Services, explicitFormat: false); + var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.LanguageServices, explicitFormat: false); // Add braces for the selected node if (addBrace) @@ -348,11 +343,11 @@ or IfStatementSyntax or ElseClauseSyntax) { // Add the braces and get the next caretPosition - var (newRoot, nextCaretPosition) = AddBraceToSelectedNode(document, root, selectedNode, formattingOptions, cancellationToken); - if (document.Project.Solution.Workspace.TryApplyChanges(document.WithSyntaxRoot(newRoot).Project.Solution)) - { - args.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(args.SubjectBuffer.CurrentSnapshot, nextCaretPosition)); - } + var (newRoot, nextCaretPosition) = AddBraceToSelectedNode(document.SolutionServices, document.Root, selectedNode, formattingOptions, cancellationToken); + + var newDocument = document.WithChangedRoot(newRoot, cancellationToken); + args.SubjectBuffer.ApplyChanges(newDocument.GetChanges(document)); + args.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(args.SubjectBuffer.CurrentSnapshot, nextCaretPosition)); } else { @@ -382,28 +377,27 @@ or IfStatementSyntax var insertionPosition = GetBraceInsertionPosition(selectedNode); // 2. Insert the braces and move caret - InsertBraceAndMoveCaret(args.TextView, document, formattingOptions, insertionPosition, cancellationToken); + InsertBraceAndMoveCaret(args.TextView, args.SubjectBuffer, document, formattingOptions, insertionPosition, cancellationToken); } } else { // Remove the braces and get the next caretPosition var (newRoot, nextCaretPosition) = RemoveBraceFromSelectedNode( - document, - root, + document.SolutionServices, + document.Root, selectedNode, formattingOptions, cancellationToken); - if (document.Project.Solution.Workspace.TryApplyChanges(document.WithSyntaxRoot(newRoot).Project.Solution)) - { - args.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(args.SubjectBuffer.CurrentSnapshot, nextCaretPosition)); - } + var newDocument = document.WithChangedRoot(newRoot, cancellationToken); + args.SubjectBuffer.ApplyChanges(newDocument.GetChanges(document)); + args.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(args.SubjectBuffer.CurrentSnapshot, nextCaretPosition)); } } private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToSelectedNode( - Document document, + SolutionServices services, SyntaxNode root, SyntaxNode selectedNode, SyntaxFormattingOptions formattingOptions, @@ -416,7 +410,7 @@ or LocalFunctionStatementSyntax or AccessorDeclarationSyntax) { var newRoot = ReplaceNodeAndFormat( - document, + services, root, selectedNode, WithBraces(selectedNode, formattingOptions), @@ -436,7 +430,7 @@ or LocalFunctionStatementSyntax { var (newNode, oldNode) = ModifyObjectCreationExpressionNode(objectCreationExpressionNode, addOrRemoveInitializer: true, formattingOptions); var newRoot = ReplaceNodeAndFormat( - document, + services, root, oldNode, newNode, @@ -487,14 +481,14 @@ or LocalFunctionStatementSyntax // In this case 'Print("Bar")' is considered as the innerStatement so when we inserted the empty block, we need also insert that if (selectedNode.IsEmbeddedStatementOwner()) { - return AddBraceToEmbeddedStatementOwner(document, root, selectedNode, formattingOptions, cancellationToken); + return AddBraceToEmbeddedStatementOwner(services, root, selectedNode, formattingOptions, cancellationToken); } throw ExceptionUtilities.UnexpectedValue(selectedNode); } private static (SyntaxNode newRoot, int nextCaretPosition) RemoveBraceFromSelectedNode( - Document document, + SolutionServices services, SyntaxNode root, SyntaxNode selectedNode, SyntaxFormattingOptions formattingOptions, @@ -513,7 +507,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) RemoveBraceFromSelect { var (newNode, oldNode) = ModifyObjectCreationExpressionNode(objectCreationExpressionNode, addOrRemoveInitializer: false, formattingOptions); var newRoot = ReplaceNodeAndFormat( - document, + services, root, oldNode, newNode, @@ -568,7 +562,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) RemoveBraceFromSelect // } // Get its no-brace version of node and insert it into the root. var newRoot = ReplaceNodeAndFormat( - document, + services, root, selectedNode, WithoutBraces(selectedNode), @@ -631,7 +625,7 @@ private static int GetBraceInsertionPosition(SyntaxNode node) CheckedStatementSyntax checkedStatementNode => checkedStatementNode.Keyword.Span.End, FieldDeclarationSyntax fieldDeclarationNode => fieldDeclarationNode.Declaration.Variables[0].Identifier.Span.End, EventFieldDeclarationSyntax eventFieldDeclarationNode => eventFieldDeclarationNode.Declaration.Variables[0].Identifier.Span.End, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; } @@ -652,7 +646,8 @@ private static string GetBracePairString(SyntaxFormattingOptions formattingOptio private void InsertBraceAndMoveCaret( ITextView textView, - Document document, + ITextBuffer buffer, + ParsedDocument document, SyntaxFormattingOptions formattingOptions, int insertionPosition, CancellationToken cancellationToken) @@ -660,19 +655,21 @@ private void InsertBraceAndMoveCaret( var bracePair = GetBracePairString(formattingOptions); // 1. Insert { }. - var newDocument = document.InsertText(insertionPosition, bracePair, cancellationToken); + var insertChange = new TextChange(new TextSpan(insertionPosition, 0), bracePair); + buffer.ApplyChange(insertChange); + var newDocument = document.WithChange(insertChange, cancellationToken); // 2. Place caret between the braces. textView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(textView.TextSnapshot, insertionPosition + 1)); // 3. Format the document using the close brace. - FormatAndApplyBasedOnEndToken(newDocument, insertionPosition + bracePair.Length - 1, formattingOptions, cancellationToken); + var changes = FormatBasedOnEndToken(newDocument, insertionPosition + bracePair.Length - 1, formattingOptions, cancellationToken); + buffer.ApplyChanges(changes); } - protected override (SyntaxNode selectedNode, bool addBrace)? GetValidNodeToModifyBraces(Document document, int caretPosition, CancellationToken cancellationToken) + protected override (SyntaxNode selectedNode, bool addBrace)? GetValidNodeToModifyBraces(ParsedDocument document, int caretPosition, CancellationToken cancellationToken) { - var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); - var token = root.FindTokenOnLeftOfPosition(caretPosition); + var token = document.Root.FindTokenOnLeftOfPosition(caretPosition); if (token.IsKind(SyntaxKind.None)) { return null; diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs index 60063fb902e03..57e22bcf11fb5 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -22,7 +23,7 @@ internal partial class AutomaticLineEnderCommandHandler #region NodeReplacementHelpers private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwnerAndInsertStatement( - Document document, + SolutionServices services, SyntaxNode root, SyntaxNode oldNode, SyntaxNode newNode, @@ -31,7 +32,6 @@ private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwner SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken) { - var services = document.Project.Solution.Services; var rootEditor = new SyntaxEditor(root, services); // 1. Insert the node before anchor node @@ -57,7 +57,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwner } private static SyntaxNode ReplaceNodeAndFormat( - Document document, + SolutionServices services, SyntaxNode root, SyntaxNode oldNode, SyntaxNode newNode, @@ -79,7 +79,7 @@ private static SyntaxNode ReplaceNodeAndFormat( var formattedNewRoot = Formatter.Format( newRoot, newNodeAfterInsertion.Span, - document.Project.Solution.Services, + services, formattingOptions, cancellationToken); @@ -91,7 +91,7 @@ private static SyntaxNode ReplaceNodeAndFormat( #region EmbeddedStatementModificationHelpers private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToEmbeddedStatementOwner( - Document document, + SolutionServices services, SyntaxNode root, SyntaxNode embeddedStatementOwner, SyntaxFormattingOptions formattingOptions, @@ -114,7 +114,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToEmbeddedSta if (statement == null || statement.IsMissing) { var newRoot = ReplaceNodeAndFormat( - document, + services, root, embeddedStatementOwner, WithBraces(embeddedStatementOwner, formattingOptions), @@ -148,7 +148,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToEmbeddedSta { WhileStatementSyntax or ForEachStatementSyntax or ForStatementSyntax or LockStatementSyntax or UsingStatementSyntax => ReplaceStatementOwnerAndInsertStatement( - document, + services, root, oldNode: embeddedStatementOwner, newNode: AddBlockToEmbeddedStatementOwner(embeddedStatementOwner, formattingOptions), @@ -156,15 +156,15 @@ WhileStatementSyntax or ForEachStatementSyntax or ForStatementSyntax or LockStat nodesToInsert: ImmutableArray.Empty.Add(statement), formattingOptions, cancellationToken), - DoStatementSyntax doStatementNode => AddBraceToDoStatement(document, root, doStatementNode, formattingOptions, statement, cancellationToken), - IfStatementSyntax ifStatementNode => AddBraceToIfStatement(document, root, ifStatementNode, formattingOptions, statement, cancellationToken), - ElseClauseSyntax elseClauseNode => AddBraceToElseClause(document, root, elseClauseNode, formattingOptions, statement, cancellationToken), + DoStatementSyntax doStatementNode => AddBraceToDoStatement(services, root, doStatementNode, formattingOptions, statement, cancellationToken), + IfStatementSyntax ifStatementNode => AddBraceToIfStatement(services, root, ifStatementNode, formattingOptions, statement, cancellationToken), + ElseClauseSyntax elseClauseNode => AddBraceToElseClause(services, root, elseClauseNode, formattingOptions, statement, cancellationToken), _ => throw ExceptionUtilities.UnexpectedValue(embeddedStatementOwner), }; } private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement( - Document document, + SolutionServices services, SyntaxNode root, DoStatementSyntax doStatementNode, SyntaxFormattingOptions formattingOptions, @@ -188,7 +188,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement && doStatementNode.CloseParenToken.IsMissing) { return ReplaceStatementOwnerAndInsertStatement( - document, + services, root, oldNode: doStatementNode, newNode: AddBlockToEmbeddedStatementOwner(doStatementNode, formattingOptions), @@ -211,7 +211,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement // Print("hello"); // } while(true); var newRoot = ReplaceNodeAndFormat( - document, + services, root, doStatementNode, AddBlockToEmbeddedStatementOwner(doStatementNode, formattingOptions, innerStatement), @@ -223,7 +223,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement } private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement( - Document document, + SolutionServices services, SyntaxNode root, IfStatementSyntax ifStatementNode, SyntaxFormattingOptions formattingOptions, @@ -243,7 +243,8 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement // Print(); if (ifStatementNode.Else == null && ifStatementNode.Parent is BlockSyntax) { - return ReplaceStatementOwnerAndInsertStatement(document, + return ReplaceStatementOwnerAndInsertStatement( + services, root, ifStatementNode, AddBlockToEmbeddedStatementOwner(ifStatementNode, formattingOptions), @@ -267,7 +268,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement // } // else {} var newRoot = ReplaceNodeAndFormat( - document, + services, root, ifStatementNode, AddBlockToEmbeddedStatementOwner(ifStatementNode, formattingOptions, innerStatement), @@ -279,7 +280,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement } private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause( - Document document, + SolutionServices services, SyntaxNode root, ElseClauseSyntax elseClauseNode, SyntaxFormattingOptions formattingOptions, @@ -290,7 +291,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause( // then treat it as the selected node is the nested if statement if (elseClauseNode.Statement is IfStatementSyntax) { - return AddBraceToEmbeddedStatementOwner(document, root, elseClauseNode.Statement, formattingOptions, cancellationToken); + return AddBraceToEmbeddedStatementOwner(services, root, elseClauseNode.Statement, formattingOptions, cancellationToken); } // Otherwise, it is just an ending else clause. @@ -310,7 +311,8 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause( // Print(); if (elseClauseNode.Parent is IfStatementSyntax { Parent: BlockSyntax }) { - return ReplaceStatementOwnerAndInsertStatement(document, + return ReplaceStatementOwnerAndInsertStatement( + services, root, elseClauseNode, WithBraces(elseClauseNode, formattingOptions), @@ -340,7 +342,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause( // els$$e // Print(); var formattedNewRoot = ReplaceNodeAndFormat( - document, + services, root, elseClauseNode, AddBlockToEmbeddedStatementOwner(elseClauseNode, formattingOptions, innerStatement), 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/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs index 23403b2c9398e..c5f53c6a64f8f 100644 --- a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs @@ -92,7 +92,14 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args) } } - IndentationOptions? lazyOptions = null; + var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + { + return false; + } + + var parsedDocument = ParsedDocument.CreateSynchronously(document, CancellationToken.None); + var options = subjectBuffer.GetIndentationOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); // We now go through the verified string literals and split each of them. // The list of spans is traversed in reverse order so we do not have to @@ -100,53 +107,39 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args) // from splitting at earlier caret positions. foreach (var span in spans.Reverse()) { - if (!SplitString(textView, subjectBuffer, span.Start.Position, ref lazyOptions, CancellationToken.None)) + using var transaction = CaretPreservingEditTransaction.TryCreate( + CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + + var splitter = StringSplitter.TryCreate(parsedDocument, span.Start.Position, options, CancellationToken.None); + if (splitter?.TrySplit(out var newRoot, out var newPosition) != true) { return false; } - } - return true; - } + // apply the change: + var newDocument = parsedDocument.WithChangedRoot(newRoot!, CancellationToken.None); + var newSnapshot = subjectBuffer.ApplyChanges(newDocument.GetChanges(parsedDocument)); + parsedDocument = newDocument; - private bool SplitString(ITextView textView, ITextBuffer subjectBuffer, int position, ref IndentationOptions? lazyOptions, CancellationToken cancellationToken) - { - var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - { - return false; - } - - lazyOptions ??= subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); - - using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); - - var parsedDocument = ParsedDocument.CreateSynchronously(document, CancellationToken.None); - var splitter = StringSplitter.TryCreate(parsedDocument, position, lazyOptions.Value, cancellationToken); - if (splitter?.TrySplit(out var newRoot, out var newPosition) != true) - { - return false; - } - - // apply the change: - var newDocument = document.WithSyntaxRoot(newRoot!); - var workspace = newDocument.Project.Solution.Workspace; - workspace.TryApplyChanges(newDocument.Project.Solution); - - // move caret: - var snapshotPoint = new SnapshotPoint( - subjectBuffer.CurrentSnapshot, newPosition); - var newCaretPoint = textView.BufferGraph.MapUpToBuffer( - snapshotPoint, PointTrackingMode.Negative, PositionAffinity.Predecessor, - textView.TextBuffer); + // The buffer edit may have adjusted to position of the current caret but we might need a different location. + // Only adjust caret if it is the only one (no multi-caret support: https://github.com/dotnet/roslyn/issues/64812). + if (spans.Count == 1) + { + var newCaretPoint = textView.BufferGraph.MapUpToBuffer( + new SnapshotPoint(newSnapshot, newPosition), + PointTrackingMode.Negative, + PositionAffinity.Predecessor, + textView.TextBuffer); + + if (newCaretPoint != null) + { + textView.Caret.MoveTo(newCaretPoint.Value); + } + } - if (newCaretPoint != null) - { - textView.Caret.MoveTo(newCaretPoint.Value); + transaction?.Complete(); } - transaction?.Complete(); return true; } diff --git a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs index aace7530b4e42..94ac4d66fbe8b 100644 --- a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs +++ b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs @@ -91,7 +91,7 @@ private static bool IsAtClosingQuote(SyntaxToken token, int position) { SyntaxKind.StringLiteralToken => position == token.Span.End - 1 && token.Text[^1] == '"', SyntaxKind.Utf8StringLiteralToken => position == token.Span.End - 3 && token.Text is [.., '"', 'u' or 'U', '8'], - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, SnapshotPoint position) diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs index 2118d42475bc7..cffa1ed465e82 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs @@ -3742,6 +3742,38 @@ void Goo() Regex.Comment("(?#comment)")); } + [Theory, WorkItem(64549, "https://github.com/dotnet/roslyn/issues/64549")] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ParamsArgument2(TestHost testHost) + { + await TestAsync( +@" +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] params string[] p) + { + } + + void Goo() + { + [|M(@""$\a(?#comment)"", @""$\a(?#comment)"");|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + [Theory] [CombinatorialData] public async Task TestRegexOnApiWithStringSyntaxAttribute_ArrayArgument(TestHost testHost) diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index e9faf5a62e018..caf5cae8344d0 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -6591,5 +6591,55 @@ static void staticLocalFunction() { }|] Punctuation.OpenCurly, Punctuation.CloseCurly); } + + [Theory] + [CombinatorialData] + public async Task ScopedParameter(TestHost testHost) + { + await TestInMethodAsync(@"interface I { void F(scoped R r); }", + testHost, + Keyword("interface"), + Interface("I"), + Punctuation.OpenCurly, + Keyword("void"), + Method("F"), + Punctuation.OpenParen, + Keyword("scoped"), + Identifier("R"), + Parameter("r"), + Punctuation.CloseParen, + Punctuation.Semicolon, + Punctuation.CloseCurly); + } + + [Theory] + [CombinatorialData] + public async Task ScopedLocalDeclaration(TestHost testHost) + { + await TestInMethodAsync(@"scoped var v;", + testHost, + Keyword("scoped"), + Identifier("var"), + Local("v"), + Punctuation.Semicolon); + } + + [Theory] + [CombinatorialData] + public async Task ScopedOutDeclaration(TestHost testHost) + { + await TestInMethodAsync(@"F(x, out scoped R y);", + testHost, + Identifier("F"), + Punctuation.OpenParen, + Identifier("x"), + Punctuation.Comma, + Keyword("out"), + Keyword("scoped"), + Identifier("R"), + Local("y"), + Punctuation.CloseParen, + Punctuation.Semicolon); + } } } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index 92c2140c56516..474e644caa259 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -491,7 +491,7 @@ void M(object o) } } }", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await new VerifyCS.Test diff --git a/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs index c81023d17436b..f70e348a9707c 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs @@ -582,7 +582,7 @@ public async Task DisabledForUnsupportedLanguageVersion(LanguageVersion language LanguageVersion.CSharp7_1 => "CS8302", LanguageVersion.CSharp7_2 => "CS8320", LanguageVersion.CSharp7_3 => "CS8370", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; // /0/Test0.cs(2,2): error [error]: Feature 'nullable reference types' is not available in C# [version]. Please use language version 8.0 or greater. diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index 3dae91a682a85..0915e99eb2b64 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -40,7 +40,7 @@ protected static string GetMarkup(string source, LanguageVersion languageVersion "; protected override TestWorkspace CreateWorkspace(string fileContents) - => TestWorkspace.CreateCSharp(fileContents, exportProvider: ExportProvider); + => TestWorkspace.CreateCSharp(fileContents, composition: GetComposition()); internal override CompletionService GetCompletionService(Project project) => Assert.IsType(base.GetCompletionService(project)); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractInteractiveCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractInteractiveCSharpCompletionProviderTests.cs index aec7ae29323d8..ea7df95aba447 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractInteractiveCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractInteractiveCSharpCompletionProviderTests.cs @@ -11,6 +11,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionPr public abstract class AbstractInteractiveCSharpCompletionProviderTests : AbstractCSharpCompletionProviderTests { protected override TestWorkspace CreateWorkspace(string fileContents) - => InteractiveCSharpTestWorkspaceFixture.CreateInteractiveWorkspace(fileContents, exportProvider: ExportProvider); + => InteractiveCSharpTestWorkspaceFixture.CreateInteractiveWorkspace(fileContents, composition: GetComposition()); } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs index 3d52fad30878e..82458c4177b0a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs @@ -427,13 +427,13 @@ public async Task CrefCompletionSpeculatesOutsideTrivia() class C { }"; - using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, exportProvider: ExportProvider); + using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, composition: GetComposition()); var called = false; var hostDocument = workspace.DocumentWithCursor; var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var service = GetCompletionService(document.Project); - var provider = Assert.IsType(service.GetTestAccessor().GetAllProviders(ImmutableHashSet.Empty).Single()); + var provider = Assert.IsType(service.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet.Empty).Single()); provider.GetTestAccessor().SetSpeculativeNodeCallback(n => { // asserts that we aren't be asked speculate on nodes inside documentation trivia. diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index baa74b81a5e59..45c2c6d4084c9 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -690,7 +690,7 @@ public async Task Parameter13() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); var options = new CompletionOptions() { @@ -2340,7 +2340,7 @@ public async Task CustomNamingStyleInsideClass() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); var options = new CompletionOptions() { @@ -2368,7 +2368,7 @@ public async Task CustomNamingStyleInsideMethod() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); var options = new CompletionOptions() { @@ -2784,7 +2784,7 @@ public async Task ConflictingLocalVariable() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); var options = new CompletionOptions() { @@ -2804,6 +2804,19 @@ void M() await VerifyItemExistsAsync(markup, "myClass1", glyph: (int)Glyph.Local, options: options); } + [Fact] + public async Task TestNotForNonTypeSymbol() + { + var markup = @" +using System; +class C +{ + Console.BackgroundColor $$ +} +"; + await VerifyItemIsAbsentAsync(markup, "consoleColor"); + } + private static NamingStylePreferences MultipleCamelCaseLocalRules() { var styles = new[] diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProviderTests.cs index 0c10fb67179c0..271507557e570 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProviderTests.cs @@ -610,6 +610,33 @@ void Goo() await VerifyNoItemsExistAsync(markup); } + [Fact] + public async Task CompletionListContainingMembers() + { + var markup = +@" +/// + public class SomeType + { } + + public static class TypeContainer + { + public static SomeType Foo1 = new SomeType(); + public static Program Foo2 = new Program(); + } + + class Program + { + void Goo() + { + SomeType c = $$ + } + }"; + await VerifyItemExistsAsync(markup, "TypeContainer"); + await VerifyItemExistsAsync(markup, "TypeContainer.Foo1"); + await VerifyItemExistsAsync(markup, "TypeContainer.Foo2"); + } + [Theory, WorkItem(828196, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/828196")] [InlineData("System.Globalization.DigitShapes")] [InlineData("System.DateTime")] diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs index 3e4c7f3671881..4b76859f74d3e 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs @@ -1787,7 +1787,7 @@ public static void Bar(this Goo goo, int x) { ReferenceType.Project => (CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), true), ReferenceType.Metadata => (CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), false), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; if (shouldContainItem) @@ -1846,7 +1846,7 @@ public static void Bar(this Goo goo, int x) (ReferenceType.Project, _) => (CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), true), (ReferenceType.Metadata, true) => (CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), false), (ReferenceType.Metadata, false) => (CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), true), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; if (shouldContainItem) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs index 6e85e47a42677..926a35e654899 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs @@ -615,7 +615,7 @@ void goo() } }"; - using var workspace = TestWorkspace.CreateCSharp(markup, exportProvider: ExportProvider); + using var workspace = TestWorkspace.CreateCSharp(markup, composition: GetComposition()); var hostDocument = workspace.Documents.Single(); var position = hostDocument.CursorPosition.Value; var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); @@ -1187,7 +1187,7 @@ static void Main(string[] args) private async Task VerifyExclusiveAsync(string markup, bool exclusive) { - using var workspace = TestWorkspace.CreateCSharp(markup, exportProvider: ExportProvider); + using var workspace = TestWorkspace.CreateCSharp(markup, composition: GetComposition()); var hostDocument = workspace.Documents.Single(); var position = hostDocument.CursorPosition.Value; var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs index 7b199df3d56ee..972a6676d6739 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs @@ -2363,7 +2363,7 @@ public override void set_Bar(int bay, int value) ", LanguageNames.CSharp, csharpFile, LanguageNames.VisualBasic, vbFile); - using var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider); + using var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition()); var testDocument = testWorkspace.Documents.Single(d => d.Name == "CSharpDocument"); Contract.ThrowIfNull(testDocument.CursorPosition); @@ -2620,7 +2620,7 @@ public override bool Equals(object obj) ", LanguageNames.CSharp, file1, file2); - using var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider); + using var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition()); var testDocument = testWorkspace.Documents.Single(d => d.Name == "CSharpDocument2"); Contract.ThrowIfNull(testDocument.CursorPosition); @@ -2676,7 +2676,7 @@ public override bool Equals(object obj) ", LanguageNames.CSharp, file2, file1); - using var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider); + using var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition()); var testDocument = testWorkspace.Documents.Single(d => d.Name == "CSharpDocument"); Contract.ThrowIfNull(testDocument.CursorPosition); @@ -2963,7 +2963,7 @@ static void Main(string[] args) override $$ } }"; - using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, exportProvider: ExportProvider); + using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, composition: GetComposition()); var provider = new OverrideCompletionProvider(); var testDocument = workspace.Documents.Single(); var document = workspace.CurrentSolution.GetRequiredDocument(testDocument.Id); @@ -3091,7 +3091,7 @@ public virtual void Bar() {} // P1 has a metadata reference to P3 and therefore doesn't get the transitive // reference to P2. If we try to override Goo, the missing "Missing" type will // prevent round tripping the symbolkey. - using var workspace = TestWorkspace.Create(text, exportProvider: ExportProvider); + using var workspace = TestWorkspace.Create(text, composition: GetComposition()); var compilation = await workspace.CurrentSolution.Projects.First(p => p.Name == "P3").GetCompilationAsync(); // CompilationExtensions is in the Microsoft.CodeAnalysis.Test.Utilities namespace @@ -3129,7 +3129,7 @@ public class SomeClass : Base "); - using var workspace = TestWorkspace.Create(source, exportProvider: ExportProvider); + using var workspace = TestWorkspace.Create(source, composition: GetComposition()); var before = @" public abstract class Base { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs index c020fb963a978..b44ab27d12f2d 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs @@ -779,7 +779,7 @@ public async Task ExpressionBodyMethod() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); workspace.GlobalOptions.SetGlobalOption( new OptionKey(CSharpCodeStyleOptions.PreferExpressionBodiedMethods), new CodeStyleOption2(ExpressionBodyPreference.WhenPossible, NotificationOption2.Silent)); @@ -810,7 +810,7 @@ public async Task ExpressionBodyMethodExtended() { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(GetComposition()); workspace.GlobalOptions.SetGlobalOption( new OptionKey(CSharpCodeStyleOptions.PreferExpressionBodiedMethods), new CodeStyleOption2(ExpressionBodyPreference.WhenPossible, NotificationOption2.Silent)); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SnippetCompletionProviderTests.cs index 62a0635cfaa89..319deaaad4e7d 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SnippetCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SnippetCompletionProviderTests.cs @@ -21,6 +21,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionPr [Trait(Traits.Feature, Traits.Features.Completion)] public class SnippetCompletionProviderTests : AbstractCSharpCompletionProviderTests { + public SnippetCompletionProviderTests() + { + ShowNewSnippetExperience = false; + } + internal override Type GetCompletionProviderType() => typeof(SnippetCompletionProvider); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs index 3dc2b4250cd03..63110bcf0a40a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs @@ -62,7 +62,7 @@ public async Task InsertPropSnippetInClassTest() var expectedCodeAfterCommit = @"class MyClass { - public int MyProperty {$$ get; set; } + public int MyProperty { get; set; }$$ }"; await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); } @@ -79,7 +79,7 @@ public async Task InsertPropSnippetInRecordTest() var expectedCodeAfterCommit = @"record MyRecord { - public int MyProperty {$$ get; set; } + public int MyProperty { get; set; }$$ }"; await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); } @@ -96,7 +96,7 @@ public async Task InsertPropSnippetInStructTest() var expectedCodeAfterCommit = @"struct MyStruct { - public int MyProperty {$$ get; set; } + public int MyProperty { get; set; }$$ }"; await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); } @@ -113,7 +113,7 @@ public async Task InsertPropSnippetInInterfaceTest() var expectedCodeAfterCommit = @"interface MyInterface { - public int MyProperty {$$ get; set; } + public int MyProperty { get; set; }$$ }"; await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); } @@ -132,7 +132,7 @@ public async Task InsertPropSnippetNamingTest() @"class MyClass { public int MyProperty { get; set; } - public int MyProperty1 {$$ get; set; } + public int MyProperty1 { get; set; }$$ }"; await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs index 6abd0c1e4c1ea..5cccd33beb447 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs @@ -1393,7 +1393,7 @@ private async Task VerifyWorkerAsync(string markup, bool isBuilder) using (var workspaceFixture = new CSharpTestWorkspaceFixture()) { - workspaceFixture.GetWorkspace(ExportProvider); + workspaceFixture.GetWorkspace(GetComposition()); var document1 = workspaceFixture.UpdateDocument(code, SourceCodeKind.Regular); await CheckResultsAsync(document1, position, isBuilder); @@ -1413,7 +1413,7 @@ private async Task CheckResultsAsync(Document document, int position, bool isBui triggerInfos.Add(CompletionTrigger.CreateDeletionTrigger('z')); var service = GetCompletionService(document.Project); - var provider = Assert.Single(service.GetTestAccessor().GetAllProviders(ImmutableHashSet.Empty)); + var provider = Assert.Single(service.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet.Empty)); foreach (var triggerInfo in triggerInfos) { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 01dedca3cc849..ebbda2ca97cdf 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -11806,11 +11806,9 @@ void local(int parameter) { } } } "; - // Speculation within attributes on local functions is broken - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - await VerifyItemExistsAsync(MakeMarkup(source), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); - await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); } [Fact] @@ -11825,11 +11823,9 @@ void local([Some(nameof(p$$))] int parameter) { } } } "; - // Speculation within attributes on local functions is broken - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - await VerifyItemExistsAsync(MakeMarkup(source), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); - await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); } [Fact] @@ -11844,11 +11840,9 @@ void M() } } "; - // Speculation within attributes on local functions is broken - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - await VerifyItemExistsAsync(MakeMarkup(source), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); - await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); } [Fact] @@ -11863,11 +11857,9 @@ void M() } } "; - // Speculation within attributes on local functions is broken - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - await VerifyItemExistsAsync(MakeMarkup(source), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); - await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); } [Fact] @@ -11888,11 +11880,20 @@ public async Task ParameterAvailableInDelegateParameterAttributeNameof() var source = @" delegate void MyDelegate([Some(nameof(p$$))] int parameter); "; - // Speculation within attributes on local functions is broken - // Tracked by https://github.com/dotnet/roslyn/issues/60801 - await VerifyItemExistsAsync(MakeMarkup(source), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); - await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter", skipSpeculation: true); + await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); + } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task AfterRequired() + { + var source = @" +class C +{ + required $$ +}"; + await VerifyAnyItemExistsAsync(source); } private static string MakeMarkup(string source, string languageVersion = "Preview") diff --git a/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordTests.cs b/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordTests.cs index 0b9cc63452930..716367c9601fc 100644 --- a/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordTests.cs @@ -4691,10 +4691,6 @@ public RefactoringTest() LanguageVersion = LanguageVersion.CSharp10; AddSolutionTransform(SolutionTransforms); MarkupOptions = MarkupOptions.UseFirstDescriptor; - CodeActionOptions = (CSharpCodeActionOptions.Default with - { - EnableConvertToRecord = true, - }).CreateProvider(); } protected override Workspace CreateWorkspaceImpl() @@ -4727,10 +4723,6 @@ public CodeFixTest() { LanguageVersion = LanguageVersion.CSharp10; AddSolutionTransform(SolutionTransforms); - CodeActionOptions = (CSharpCodeActionOptions.Default with - { - EnableConvertToRecord = true, - }).CreateProvider(); } } diff --git a/src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs b/src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs index 874aa6960ab6a..c9b23365b51d0 100644 --- a/src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs @@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertTupleToStruct public class ConvertTupleToStructTests { private static OptionsCollection PreferImplicitTypeWithInfo() - => new OptionsCollection(LanguageNames.CSharp) + => new(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Suggestion }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, true, NotificationOption2.Suggestion }, diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs index 4fc03f6a46757..4e173ba05956f 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs @@ -740,6 +740,39 @@ private void Goo(out object a, ref object b) }"); } + [Fact, WorkItem(54212, "https://github.com/dotnet/roslyn/issues/54212")] + public async Task TestInArgument() + { + await TestInRegularAndScriptAsync( +@" +using System; + +class Example +{ + void M() + { + int i = 0; + [|M2(in i)|]; + } +}", +@" +using System; + +class Example +{ + void M() + { + int i = 0; + [|M2(in i)|]; + } + + private void M2(in int i) + { + throw new NotImplementedException(); + } +}"); + } + [Fact] public async Task TestMemberAccessArgumentName() { diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs index 3b57d67473bcd..461d2d92d6aa9 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs @@ -681,5 +681,17 @@ void IInterface.this await TestInRegularAndScriptAsync(text, expected); } + + [Fact, WorkItem(1640728, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1640728")] + public async Task TestMisspelledWordThatIsAlsoSnippetName() + { + await TestInRegularAndScriptAsync( +@"public [|interfacce|] IWhatever +{ +}", +@"public interface IWhatever +{ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs index 6d51aec1cd24f..ea9e1cc6c2958 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs @@ -172,7 +172,7 @@ public async Task TrackingService_GetLatestSpansAsync(bool scheduleInitialTracki new ActiveStatementSpan(0, span21, ActiveStatementFlags.NonLeafFrame, unmappedDocumentId: null), new ActiveStatementSpan(1, span22, ActiveStatementFlags.LeafFrame, unmappedDocumentId: null)), "2.cs" => ImmutableArray.Empty, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; var testDocument1 = new TestHostDocument(text: source1, displayName: "1.cs", exportProvider: workspace.ExportProvider, filePath: "1.cs"); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index 8618ad852b15f..995bf02284893 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -507,7 +507,7 @@ public static void Main() var experimental = TestOptions.Regular.WithFeatures(featuresToEnable); using var workspace = TestWorkspace.CreateCSharp( - source1, parseOptions: experimental, compilationOptions: null, exportProvider: null); + source1, parseOptions: experimental, compilationOptions: null); var oldSolution = workspace.CurrentSolution; var oldProject = oldSolution.Projects.Single(); diff --git a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs index 7756de40437ea..43285ef18724e 100644 --- a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs +++ b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs @@ -131,8 +131,9 @@ protected static async Task ExtractMethodAsync( var document = workspace.CurrentSolution.GetDocument(testDocument.Id); Assert.NotNull(document); - var options = new ExtractMethodGenerationOptions(CodeGenerationOptions.GetDefault(document.Project.Services)) + var options = new ExtractMethodGenerationOptions() { + CodeGenerationOptions = CodeGenerationOptions.GetDefault(document.Project.Services), ExtractOptions = new() { DontPutOutOrRefOnStruct = dontPutOutOrRefOnStruct } }; diff --git a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs index 8e801c0ddd4e7..cd5f5e8c6ef8c 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs @@ -3,24 +3,15 @@ // 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.CSharp.Formatting; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.TodoComments; -using Microsoft.CodeAnalysis.UnitTests; -using Microsoft.CodeAnalysis.VisualBasic.Formatting; using Roslyn.Test.Utilities; using Xunit; @@ -62,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/Interactive/NavigateTo/InteractiveNavigateToTests.cs b/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs index 68a107bfef5c1..4846af4d2ed69 100644 --- a/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs @@ -24,8 +24,8 @@ public class InteractiveNavigateToTests : AbstractNavigateToTests { protected override string Language => "csharp"; - protected override TestWorkspace CreateWorkspace(string content, ExportProvider exportProvider) - => TestWorkspace.CreateCSharp(content, parseOptions: Options.Script, exportProvider: exportProvider); + protected override TestWorkspace CreateWorkspace(string content, TestComposition composition) + => TestWorkspace.CreateCSharp(content, parseOptions: Options.Script, composition: composition); [WpfTheory] [CombinatorialData] diff --git a/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs b/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs index 229fd7082809c..04a56bb99e875 100644 --- a/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs +++ b/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs @@ -1291,5 +1291,174 @@ int M() } }", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9)); } + + [Fact, WorkItem(63311, "https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThan() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(int? p) + { + [||]if (p > 10) + { + System.Console.WriteLine("p is not null and p.Value > 10"); + } + } + } + """, + """ + class C + { + void M(int? p) + { + if (!(p > 10)) + { + return; + } + System.Console.WriteLine("p is not null and p.Value > 10"); + } + } + """); + } + + [Fact, WorkItem(63311, "https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThanOrEqual() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(int? p) + { + [||]if (p >= 10) + { + System.Console.WriteLine("p is not null and p.Value >= 10"); + } + } + } + """, + """ + class C + { + void M(int? p) + { + if (!(p >= 10)) + { + return; + } + System.Console.WriteLine("p is not null and p.Value >= 10"); + } + } + """); + } + + [Fact, WorkItem(63311, "https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThan() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(int? p) + { + [||]if (p < 10) + { + System.Console.WriteLine("p is not null and p.Value < 10"); + } + } + } + """, + """ + class C + { + void M(int? p) + { + if (!(p < 10)) + { + return; + } + System.Console.WriteLine("p is not null and p.Value < 10"); + } + } + """); + } + + [Fact, WorkItem(63311, "https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThanOrEqual() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(int? p) + { + [||]if (p <= 10) + { + System.Console.WriteLine("p is not null and p.Value <= 10"); + } + } + } + """, + """ + class C + { + void M(int? p) + { + if (!(p <= 10)) + { + return; + } + System.Console.WriteLine("p is not null and p.Value <= 10"); + } + } + """); + } + + [Fact, WorkItem(63311, "https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestNullableReference_GreaterThan() + { + await TestInRegularAndScriptAsync( + """ + #nullable enable + using System; + class C + { + void M(C? p) + { + [||]if (p > new C()) + { + Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); + } + } + + public static bool operator <(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >(C? left, C? right) => throw new NotImplementedException(); + public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); + } + """, + """ + #nullable enable + using System; + class C + { + void M(C? p) + { + if (p <= new C()) + { + return; + } + Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); + } + + public static bool operator <(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >(C? left, C? right) => throw new NotImplementedException(); + public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); + } + """); + } } } diff --git a/src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs b/src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs index 796c347bfc008..982a29b060689 100644 --- a/src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs +++ b/src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs @@ -495,7 +495,27 @@ void M(bool x, int a, object b) } [Fact, WorkItem(42368, "https://github.com/dotnet/roslyn/issues/42368")] - public async Task InvertIsTruePattern1_CSharp9() + public async Task InvertBooleanIsTruePattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, object b) + { + var c = a > 10 [||]&& x is true; + } +}", +@"class C +{ + void M(bool x, int a, object b) + { + var c = !(a <= 10 || x is false); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64292, "https://github.com/dotnet/roslyn/issues/64292")] + public async Task InvertNonBooleanIsTruePattern1_CSharp9() { await TestInRegularAndScriptAsync( @"class C @@ -509,7 +529,7 @@ void M(bool x, int a, object b) { void M(bool x, int a, object b) { - var c = !(a <= 10 || b is false); + var c = !(a <= 10 || b is not true); } }", parseOptions: CSharp9); } @@ -535,7 +555,27 @@ void M(bool x, int a, object b) } [Fact, WorkItem(42368, "https://github.com/dotnet/roslyn/issues/42368")] - public async Task InvertIsFalsePattern1_CSharp9() + public async Task InvertBooleanIsFalsePattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, object b) + { + var c = a > 10 [||]&& x is false; + } +}", +@"class C +{ + void M(bool x, int a, object b) + { + var c = !(a <= 10 || x is true); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64292, "https://github.com/dotnet/roslyn/issues/64292")] + public async Task InvertNonBooleanIsFalsePattern1_CSharp9() { await TestInRegularAndScriptAsync( @"class C @@ -549,7 +589,87 @@ void M(bool x, int a, object b) { void M(bool x, int a, object b) { - var c = !(a <= 10 || b is true); + var c = !(a <= 10 || b is not false); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64558, "https://github.com/dotnet/roslyn/issues/64558")] + public async Task InvertNumericIsGreaterThanPattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, object b) + { + var c = a > 10 [||]&& a is > 20; + } +}", +@"class C +{ + void M(bool x, int a, object b) + { + var c = !(a <= 10 || a is <= 20); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64558, "https://github.com/dotnet/roslyn/issues/64558")] + public async Task InvertNullableNumericIsGreaterThanPattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int? a, object b) + { + var c = x [||]&& a is > 20; + } +}", +@"class C +{ + void M(bool x, int? a, object b) + { + var c = !(!x || a is not > 20); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64558, "https://github.com/dotnet/roslyn/issues/64558")] + public async Task InvertNonNumericIsGreaterThanPattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, object b) + { + var c = a > 10 [||]&& b is > 20; + } +}", +@"class C +{ + void M(bool x, int a, object b) + { + var c = !(a <= 10 || b is not > 20); + } +}", parseOptions: CSharp9); + } + + [Fact, WorkItem(64558, "https://github.com/dotnet/roslyn/issues/64558")] + public async Task InvertInvalidEqualsPattern1_CSharp9() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M(bool x, int a, object b) + { + var c = a > 10 [||]&& a is == 20; + } +}", +@"class C +{ + void M(bool x, int a, object b) + { + var c = !(a <= 10 || a is not == 20); } }", parseOptions: CSharp9); } diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs index 98ac7c4f2af52..4b21b39cfee9b 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -38,37 +39,14 @@ private static void SetupSearchProject( pattern, ImmutableHashSet.Empty, It.IsAny(), - It.IsAny>(), - It.IsAny())).Callback( - (Project project, - ImmutableArray priorityDocuments, - string pattern, - IImmutableSet kinds, - Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) => - { - if (result != null) - onResultFound(result); - }).Returns(Task.CompletedTask); + It.IsAny())).Returns(GetEnumerable(result)); searchService.Setup(ss => ss.SearchGeneratedDocumentsAsync( It.IsAny(), pattern, ImmutableHashSet.Empty, It.IsAny(), - It.IsAny>(), - It.IsAny())).Callback( - (Project project, - string pattern, - IImmutableSet kinds, - Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) => - { - if (result != null) - onResultFound(result); - }).Returns(Task.CompletedTask); + It.IsAny())).Returns(GetEnumerable(result)); // Followed by a generated doc search. } @@ -80,22 +58,18 @@ private static void SetupSearchProject( pattern, ImmutableHashSet.Empty, It.IsAny(), - It.IsAny>(), - It.IsAny())).Callback( - (Project project, - ImmutableArray priorityDocuments, - string pattern2, - IImmutableSet kinds, - Document? activeDocument, - Func onResultFound2, - CancellationToken cancellationToken) => - { - if (result != null) - onResultFound2(result); - }).Returns(Task.CompletedTask); + It.IsAny())).Returns(GetEnumerable(result)); } } +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + private static async IAsyncEnumerable GetEnumerable(INavigateToSearchResult? result) +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + if (result != null) + yield return result; + } + private static ValueTask IsFullyLoadedAsync(bool projectSystem, bool remoteHost) => new(projectSystem && remoteHost); diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs index b6f9c3735c528..30aeb0a25daf4 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs @@ -28,8 +28,8 @@ public class NavigateToTests : AbstractNavigateToTests { protected override string Language => "csharp"; - protected override TestWorkspace CreateWorkspace(string content, ExportProvider exportProvider) - => TestWorkspace.CreateCSharp(content, exportProvider: exportProvider); + protected override TestWorkspace CreateWorkspace(string content, TestComposition composition) + => TestWorkspace.CreateCSharp(content, composition: composition); [Theory] [CombinatorialData] diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs index 978ccf642e208..03eb55ffab3dc 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs @@ -157,9 +157,7 @@ protected static async Task GenerateFileAndVerifyAsync( var masWorkspace = service.TryGetWorkspace(); - var document = masWorkspace!.CurrentSolution.Projects.First().Documents.First(); - - Assert.Equal(document.FilePath, file.FilePath); + var document = masWorkspace!.CurrentSolution.Projects.First().Documents.First(d => d.FilePath == file.FilePath); // Mapping the project from the generated document should map back to the original project var provider = workspace.ExportProvider.GetExportedValues().OfType().Single(); @@ -251,6 +249,11 @@ protected static void CompileTestSource(string path, SourceText source, Project } protected static void CompileTestSource(string dllFilePath, string sourceCodePath, string? pdbFilePath, string assemblyName, SourceText source, Project project, Location pdbLocation, Location sourceLocation, bool buildReferenceAssembly, bool windowsPdb, Encoding? fallbackEncoding = null) + { + CompileTestSource(dllFilePath, new[] { sourceCodePath }, pdbFilePath, assemblyName, new[] { source }, project, pdbLocation, sourceLocation, buildReferenceAssembly, windowsPdb, fallbackEncoding); + } + + protected static void CompileTestSource(string dllFilePath, string[] sourceCodePaths, string? pdbFilePath, string assemblyName, SourceText[] sources, Project project, Location pdbLocation, Location sourceLocation, bool buildReferenceAssembly, bool windowsPdb, Encoding? fallbackEncoding = null) { var compilationFactory = project.Solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); var options = compilationFactory.GetDefaultCompilationOptions().WithOutputKind(OutputKind.DynamicallyLinkedLibrary); @@ -258,7 +261,7 @@ protected static void CompileTestSource(string dllFilePath, string sourceCodePat var compilation = compilationFactory .CreateCompilation(assemblyName, options) - .AddSyntaxTrees(SyntaxFactory.ParseSyntaxTree(source, options: parseOptions, path: sourceCodePath)) + .AddSyntaxTrees(sources.Select((s, i) => SyntaxFactory.ParseSyntaxTree(s, options: parseOptions, path: sourceCodePaths[i]))) .AddReferences(project.MetadataReferences); IEnumerable? embeddedTexts; @@ -269,11 +272,14 @@ protected static void CompileTestSource(string dllFilePath, string sourceCodePat else if (sourceLocation == Location.OnDisk) { embeddedTexts = null; - File.WriteAllText(sourceCodePath, source.ToString(), source.Encoding); + for (var i = 0; i < sources.Length; i++) + { + File.WriteAllText(sourceCodePaths[i], sources[i].ToString(), sources[i].Encoding); + } } else { - embeddedTexts = new[] { EmbeddedText.FromSource(sourceCodePath, source) }; + embeddedTexts = sources.Select((s, i) => EmbeddedText.FromSource(sourceCodePaths[i], s)).ToArray(); } EmitOptions emitOptions; @@ -300,7 +306,7 @@ protected static void CompileTestSource(string dllFilePath, string sourceCodePat if (fallbackEncoding is null) { - emitOptions = emitOptions.WithDefaultSourceFileEncoding(source.Encoding); + emitOptions = emitOptions.WithDefaultSourceFileEncoding(sources[0].Encoding); } else { diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs index efa27a046e766..dc04c2f89b337 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs @@ -32,7 +32,7 @@ public NullResultMetadataAsSourceFileProvider() { } - public void CleanupGeneratedFiles(Workspace? workspace) + public void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace) { } @@ -46,17 +46,17 @@ public void CleanupGeneratedFiles(Workspace? workspace) return null; } - public bool TryAddDocumentToWorkspace(Workspace workspace, string filePath, Text.SourceTextContainer sourceTextContainer) + public bool TryAddDocumentToWorkspace(MetadataAsSourceWorkspace workspace, string filePath, Text.SourceTextContainer sourceTextContainer) { return true; } - public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) + public bool TryRemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, string filePath) { return true; } - public bool ShouldCollapseOnOpen(string filePath, BlockStructureOptions options) + public bool ShouldCollapseOnOpen(MetadataAsSourceWorkspace workspace, string filePath, BlockStructureOptions options) { return true; } 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/CSharpTest/PdbSourceDocument/PdbSourceDocumentTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentTests.cs index 9f7ad511f8be4..ea5ac5d35666d 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentTests.cs @@ -882,5 +882,61 @@ await RunTestAsync(async path => } }); } + + [Fact] + public async Task MethodInPartialType_NavigateToCorrectFile() + { + var source1 = @" +public partial class C +{ + public void M1() + { + } +} +"; + var source2 = @" +using System.Threading.Tasks; + +public partial class C +{ + public static async Task [|M2|]() => await M3(); + + private static async Task M3() + { + } +} +"; + + await RunTestAsync(async path => + { + MarkupTestFile.GetSpan(source2, out source2, out var expectedSpan); + + var sourceText1 = SourceText.From(source1, Encoding.UTF8); + var sourceText2 = SourceText.From(source2, Encoding.UTF8); + + var workspace = TestWorkspace.Create(@$" + + + +", composition: GetTestComposition()); + + var project = workspace.CurrentSolution.Projects.First(); + + var dllFilePath = GetDllPath(path); + var sourceCodePath = GetSourceFilePath(path); + var pdbFilePath = GetPdbPath(path); + CompileTestSource(dllFilePath, new[] { Path.Combine(path, "source1.cs"), Path.Combine(path, "source2.cs") }, pdbFilePath, "reference", new[] { sourceText1, sourceText2 }, project, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false); + + project = project.AddMetadataReference(MetadataReference.CreateFromFile(GetDllPath(path))); + + var mainCompilation = await project.GetRequiredCompilationAsync(CancellationToken.None).ConfigureAwait(false); + + var symbol = mainCompilation.GetMember("C.M2"); + + AssertEx.NotNull(symbol, $"Couldn't find symbol to go-to-def for."); + + await GenerateFileAndVerifyAsync(project, symbol, Location.Embedded, source2.ToString(), expectedSpan, expectNullResult: false); + }); + } } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 9ad3b5efa692e..022f881214973 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -8084,7 +8084,7 @@ void M() $$var v = (ref int i) => i.ToString(); } }", - MainDescription("delegate string (ref int)"), + MainDescription("delegate string (ref int arg)"), AnonymousTypes("")); } @@ -8103,7 +8103,7 @@ void M() AnonymousTypes( $@" {FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int)")); + 'a {FeaturesResources.is_} delegate string (ref int arg)")); } [Fact, WorkItem(58871, "https://github.com/dotnet/roslyn/issues/58871")] @@ -8124,7 +8124,7 @@ void M() AnonymousTypes( $@" {FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int)")); + 'a {FeaturesResources.is_} delegate string (ref int arg)")); } [Fact, WorkItem(61320, "https://github.com/dotnet/roslyn/issues/61320")] @@ -8394,5 +8394,44 @@ void local(string s) { } await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } + + [Fact] + public async Task TestScopedParameter() + { + var source = +@"ref struct R { } +class Program +{ + static void F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, scoped out R r8) + { + r7 = default; + r8 = default; + } + static void Main() + { + R r = default; + $$F(r, r, ref r, ref r, r, r, out r, out r); + } +}"; + await TestAsync(source, + MainDescription($"void Program.F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, out R r8)")); + } + + [Fact] + public async Task TestScopedLocal() + { + var source = +@"class Program +{ + static void Main() + { + int i = 0; + scoped ref int r = ref i; + i = $$r; + } +}"; + await TestAsync(source, + MainDescription($"({FeaturesResources.local_variable}) scoped ref int r")); + } } } diff --git a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs index ca9610e322cda..6d0f14c70e24d 100644 --- a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs @@ -72,14 +72,15 @@ private static void TestWorker( var originalSnapshot = textBuffer.CurrentSnapshot; var originalSelections = document.SelectedSpans; - var snapshotSpans = new List(); - foreach (var selection in originalSelections) + // primary caret will be the last one: + view.SetMultiSelection(originalSelections.Select(selection => selection.ToSnapshotSpan(originalSnapshot))); + + // only validate when there is no selected text since the splitter is disabled in that case: + if (originalSelections.All(selection => selection.IsEmpty)) { - snapshotSpans.Add(selection.ToSnapshotSpan(originalSnapshot)); + Assert.Equal(originalSelections.Last().Start, view.Caret.Position.BufferPosition.Position); } - view.SetMultiSelection(snapshotSpans); - var undoHistoryRegistry = workspace.GetService(); var commandHandler = workspace.ExportProvider.GetCommandHandler(nameof(SplitStringLiteralCommandHandler)); @@ -94,7 +95,7 @@ private static void TestWorker( out var expectedOutput, out ImmutableArray expectedSpans); Assert.Equal(expectedOutput, textBuffer.CurrentSnapshot.AsText().ToString()); - Assert.Equal(expectedSpans.First().Start, view.Caret.Position.BufferPosition.Position); + Assert.Equal(expectedSpans.Last().Start, view.Caret.Position.BufferPosition.Position); if (verifyUndo) { @@ -104,7 +105,7 @@ private static void TestWorker( var currentSnapshot = document.GetTextBuffer().CurrentSnapshot; Assert.Equal(originalSnapshot.GetText(), currentSnapshot.GetText()); - Assert.Equal(originalSelections.First().Start, view.Caret.Position.BufferPosition.Position); + Assert.Equal(originalSelections.Last().Start, view.Caret.Position.BufferPosition.Position); } } } diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index 6c8147d0e817a..4b1cad93c3b5b 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -145,7 +145,7 @@ void Test() var expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( - new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), + new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, CSharpAnalyzersResources.Using_directive_is_unnecessary))); @@ -158,7 +158,7 @@ void Test() expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( - new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), + new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, CSharpAnalyzersResources.Using_directive_is_unnecessary))); @@ -184,7 +184,7 @@ void Test() expectedToolTip = new ContainerElement( ContainerElementStyle.Wrapped, new ClassifiedTextElement( - new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0049", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049", UriKind.Absolute)), "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049"), + new ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0049", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049", UriKind.Absolute)), "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049"), new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), new ClassifiedTextRun(ClassificationTypeNames.Text, AnalyzersResources.Name_can_be_simplified))); diff --git a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs index 462b5351fa5ab..0abe870619900 100644 --- a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs @@ -38,7 +38,7 @@ internal override async Task> GetBlockSpansWorkerAsync return CSharpStructureHelpers.CreateCommentBlockSpan(token.TrailingTrivia); } - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs index 3d7d3cf6428c4..6513a6bedeabd 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs @@ -39,7 +39,7 @@ private static TestWorkspace CreateWorkspace( TestComposition composition = null) { composition ??= EditorTestCompositions.EditorFeatures; - return new TestWorkspace(exportProvider: null, composition, workspaceKind, disablePartialSolutions: disablePartialSolutions); + return new TestWorkspace(composition, workspaceKind, disablePartialSolutions: disablePartialSolutions); } private static async Task WaitForWorkspaceOperationsToComplete(TestWorkspace workspace) diff --git a/src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs index f3c1a3fc33a8f..a3ce25159c60d 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs @@ -897,6 +897,50 @@ public C() : base(a, b, c) { } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingStartToken1() + { + await TestMissingAsync( +@"class C { + void Bar() { + Goobar [||]) + } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingStartToken2() + { + await TestMissingAsync( +@"class C { + void Bar() { + Goobar [||]i, j) + } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingEndToken1() + { + await TestMissingAsync( +@"class C { + void Bar() { + Goobar([||] + } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingEndToken2() + { + await TestMissingAsync( +@"class C { + void Bar() { + Goobar([||]i, j + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs index 341718ea8b364..af22163a07547 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs @@ -572,5 +572,39 @@ await TestAllWrappingCasesAsync( }" ); } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingStartToken() + { + await TestMissingAsync( +@"class C { + void Bar() { + var test = new[] [||] 1, 2 }; + } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingEndToken1() + { + await TestMissingAsync( +@"class C { + void Bar() { + var test = new[] [||]{ 1, 2 + return; + } +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestMissingEndToken2() + { + await TestMissingAsync( +@"class C { + void Bar() { + var test = new[] [||]{ 1, 2 ; + } +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs index 6ba980ef9effe..ae9a8254718f5 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs @@ -984,6 +984,42 @@ public async Task TestWithMissingParameterList() await TestMissingAsync( @"class C { public void UpsertRecord[||] +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestWithMissingStartToken1() + { + await TestMissingAsync( +@"class C { + public void UpsertRecord[||]) +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestWithMissingStartToken2() + { + await TestMissingAsync( +@"class C { + public void UpsertRecord[||] int i, int j) +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestWithMissingEndToken1() + { + await TestMissingAsync( +@"class C { + public void UpsertRecord([||] +}"); + } + + [Fact, WorkItem(63732, "https://github.com/dotnet/roslyn/issues/63732")] + public async Task TestWithMissingEndToken2() + { + await TestMissingAsync( +@"class C { + public void UpsertRecord([||]int i, int j }"); } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs index eab73764ba834..2ceeaa92c1524 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs @@ -789,5 +789,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs index 21c44e68d95d1..b8aff259dfea4 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs @@ -790,5 +790,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs index 19deb320d6c5e..c02ccce2d9718 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs @@ -829,5 +829,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs index a4b0ef9587d41..780f27840012f 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs @@ -781,5 +781,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs index eed20056ee1f3..952c936337ff8 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs index f05ea3c182936..4a0b5d88dbf26 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs @@ -740,5 +740,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs index 88f7929937cce..e37d084789a3f 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs @@ -854,5 +854,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs index 3d78a3233d878..873ca30570cfc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs index c247b03de82dd..1f25aad3f428b 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs @@ -373,5 +373,15 @@ public async Task TestNotInPreprocessorDirective() await VerifyAbsenceAsync( @"#$$"); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs index 51fa74911d417..af410741c1ca2 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs @@ -788,5 +788,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs index 3cdf02542583e..22874eddc4592 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs index 818cb86e1db07..233aa121bdc35 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index d2cb8721939bb..52b3cd0ee7328 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -803,5 +803,15 @@ class C { delegate*$$"); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs index a67a29692bdc6..3cbff26c832e8 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs @@ -421,5 +421,17 @@ void Goo() where T : IList<$$ where U : T"); } + + [Fact, WorkItem(64465, "https://github.com/dotnet/roslyn/issues/64465")] + public async Task TestNotAfterRecord_AbstractModifier() + { + await VerifyAbsenceAsync("abstract record $$"); + } + + [Fact, WorkItem(64465, "https://github.com/dotnet/roslyn/issues/64465")] + public async Task TestNotAfterRecord_SealedModifier() + { + await VerifyAbsenceAsync("sealed record $$"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs index e235bde4a0f4d..371721df65dde 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs index 85fd94c32c81c..d0030a3272468 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs index 7b73c1cfdb7ce..67430834633af 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs @@ -767,5 +767,15 @@ public async Task TestNotAfterKeywordIndicatingLocalFunctionWithAsync(string key await VerifyAbsenceAsync(AddInsideMethod($@" {keyword} $$")); } + + [Fact, WorkItem(64585, "https://github.com/dotnet/roslyn/issues/64585")] + public async Task TestAfterRequired() + { + await VerifyKeywordAsync(@" +class C +{ + required $$ +}"); + } } } diff --git a/src/EditorFeatures/Core.Cocoa/Lsp/VSMacLspLoggerFactoryWrapper.cs b/src/EditorFeatures/Core.Cocoa/Lsp/VSMacLspLoggerFactoryWrapper.cs index 4f33afef6eae2..c478c881d089b 100644 --- a/src/EditorFeatures/Core.Cocoa/Lsp/VSMacLspLoggerFactoryWrapper.cs +++ b/src/EditorFeatures/Core.Cocoa/Lsp/VSMacLspLoggerFactoryWrapper.cs @@ -15,10 +15,10 @@ namespace Microsoft.CodeAnalysis.EditorFeatures.Cocoa.Lsp; /// /// Wraps the external access and exports it -/// as an for inclusion in the vsmac composition. +/// as an for inclusion in the vsmac composition. /// -[Export(typeof(ILspLoggerFactory)), Shared] -internal class VSMacLspLoggerFactoryWrapper : ILspLoggerFactory +[Export(typeof(ILspServiceLoggerFactory)), Shared] +internal class VSMacLspLoggerFactoryWrapper : ILspServiceLoggerFactory { private readonly IVSMacLspLoggerFactory _loggerFactory; @@ -29,14 +29,14 @@ public VSMacLspLoggerFactoryWrapper(IVSMacLspLoggerFactory loggerFactory) _loggerFactory = loggerFactory; } - public async Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken) + public async Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken) { var vsMacLogger = await _loggerFactory.CreateLoggerAsync(serverTypeName, jsonRpc, cancellationToken).ConfigureAwait(false); return new VSMacLspLoggerWrapper(vsMacLogger); } } -internal class VSMacLspLoggerWrapper : ILspLogger +internal class VSMacLspLoggerWrapper : ILspServiceLogger { private readonly IVSMacLspLogger _logger; @@ -45,15 +45,33 @@ public VSMacLspLoggerWrapper(IVSMacLspLogger logger) _logger = logger; } - public void TraceError(string message) => _logger.TraceError(message); + public void LogError(string message, params object[] @params) + { + _logger.TraceError(message); + } - public void TraceException(Exception exception) => _logger.TraceException(exception); + public void LogException(Exception exception, string? message = null, params object[] @params) + { + _logger.TraceException(exception); + } - public void TraceInformation(string message) => _logger.TraceInformation(message); + public void LogInformation(string message, params object[] @params) + { + _logger.TraceInformation(message); + } - public void TraceStart(string message) => _logger.TraceStart(message); + public void LogStartContext(string message, params object[] @params) + { + _logger.TraceStart(message); + } - public void TraceStop(string message) => _logger.TraceStop(message); + public void LogEndContext(string message, params object[] @params) + { + _logger.TraceStop(message); + } - public void TraceWarning(string message) => _logger.TraceWarning(message); + public void LogWarning(string message, params object[] @params) + { + _logger.TraceWarning(message); + } } diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index f6a424427dff3..2313320b40756 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -121,5 +121,19 @@ diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error && return null; } } + + /// + /// TODO: is there anything we can do better here? Inline diagnostic tags are not really data, but more UI + /// elements with specific constrols, positions and events attached to them. There doesn't seem to be a safe + /// way to reuse any of these currently. Ideally we could do something similar to inline-hints where there's a + /// data tagger portion (which is async and has clean equality semantics), and then the UI portion which just + /// translates those data-tags to the UI tags. + /// + /// Doing direct equality means we'll always end up regenerating all tags. But hopefully there won't be that + /// many in a document to matter. + /// + /// + protected override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2) + => tag1 == tag2; } } diff --git a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs index 66b4608e91268..cafa1a6a4b06a 100644 --- a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs +++ b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs @@ -196,7 +196,7 @@ public IEnumerable> GetTags(NormalizedSnapshotSp } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs index 3e86f9f8a8523..a3e91f94d651a 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs @@ -260,7 +260,7 @@ public async Task ExecuteCodeAsync(string text) } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowResetCommand.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowResetCommand.cs index 3179927f9321f..187410604ac0f 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowResetCommand.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowResetCommand.cs @@ -176,7 +176,7 @@ internal static string GetCommandLine(bool initialize, InteractiveHostPlatform? InteractiveHostPlatform.Core => " " + PlatformCore, InteractiveHostPlatform.Desktop64 => " " + PlatformDesktop64, InteractiveHostPlatform.Desktop32 => " " + PlatformDesktop32, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; private void ReportInvalidArguments(IInteractiveWindow window) diff --git a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTaggerProvider.cs index 5c46c2526ce74..dbe4cae017445 100644 --- a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTaggerProvider.cs @@ -114,5 +114,12 @@ protected override async Task ProduceTagsAsync( context.AddTag(new TagSpan(span.ToSnapshotSpan(snapshotSpan.Snapshot), tag)); } } + + /// + /// We create and cache a separator tag to use (unless the format mapping changes). So we can just use identity + /// comparisons here. + /// + protected override bool TagEquals(LineSeparatorTag tag1, LineSeparatorTag tag2) + => tag1 == tag2; } } diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs index 93b6e9b990c46..e08b50aed307f 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs @@ -127,7 +127,7 @@ private async Task ComputeModelInBackgroundAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTag.cs b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTag.cs index c3aa636a2ce4e..989560196a8bb 100644 --- a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTag.cs +++ b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTag.cs @@ -2,27 +2,33 @@ // 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.Windows.Media; using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.StringIndentation { /// /// Tag that specifies how a string's content is indented. /// - internal class StringIndentationTag : BrushTag + internal class StringIndentationTag : BrushTag, IEquatable { + private readonly StringIndentationTaggerProvider _provider; + public readonly ImmutableArray OrderedHoleSpans; public StringIndentationTag( + StringIndentationTaggerProvider provider, IEditorFormatMap editorFormatMap, ImmutableArray orderedHoleSpans) : base(editorFormatMap) { + _provider = provider; OrderedHoleSpans = orderedHoleSpans; } @@ -31,5 +37,28 @@ public StringIndentationTag( var brush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; return brush?.Color; } + + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => Equals(obj as StringIndentationTag); + + public bool Equals(StringIndentationTag? other) + { + if (other is null) + return false; + + if (this.OrderedHoleSpans.Length != other.OrderedHoleSpans.Length) + return false; + + for (int i = 0, n = this.OrderedHoleSpans.Length; i < n; i++) + { + if (!_provider.SpanEquals(this.OrderedHoleSpans[i], other.OrderedHoleSpans[i])) + return false; + } + + return true; + } } } diff --git a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTaggerProvider.cs index fca7f5d3149ff..8df3ef466aa1a 100644 --- a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationTaggerProvider.cs @@ -69,7 +69,7 @@ public StringIndentationTaggerProvider( /// then the span of the tag will grow to the right and the line will immediately redraw in the correct position /// while we're in the process of recomputing the up to date tags. /// - protected override SpanTrackingMode SpanTrackingMode => SpanTrackingMode.EdgeInclusive; + public override SpanTrackingMode SpanTrackingMode => SpanTrackingMode.EdgeInclusive; protected override ITaggerEventSource CreateEventSource( ITextView? textView, ITextBuffer subjectBuffer) @@ -113,9 +113,13 @@ protected override async Task ProduceTagsAsync( context.AddTag(new TagSpan( region.IndentSpan.ToSnapshotSpan(snapshot), new StringIndentationTag( + this, _editorFormatMap, region.OrderedHoleSpans.SelectAsArray(s => s.ToSnapshotSpan(snapshot))))); } } + + protected override bool TagEquals(StringIndentationTag tag1, StringIndentationTag tag2) + => tag1.Equals(tag2); } } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 91b48d7373ada..058fa5514a826 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -242,7 +242,7 @@ ISuggestedAction ConvertToSuggestedAction(IUnifiedSuggestedAction unifiedSuggest ThreadingContext, owner, nestedAction.Workspace, originalSolution, subjectBuffer, nestedAction.Provider ?? this, nestedAction.OriginalCodeAction, nestedAction.NestedActionSets.SelectAsArray((s, arg) => ConvertToSuggestedActionSet(s, arg.owner, arg.subjectBuffer), (owner, subjectBuffer))), - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(UnifiedSuggestedActionSetPriority unifiedSuggestedActionSetPriority) @@ -252,7 +252,7 @@ static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(UnifiedSug UnifiedSuggestedActionSetPriority.Low => SuggestedActionSetPriority.Low, UnifiedSuggestedActionSetPriority.Medium => SuggestedActionSetPriority.Medium, UnifiedSuggestedActionSetPriority.High => SuggestedActionSetPriority.High, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; } @@ -292,7 +292,7 @@ private static string GetFixCategory(DiagnosticSeverity severity) case DiagnosticSeverity.Error: return PredefinedSuggestedActionCategoryNames.ErrorFix; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs index 5af0a94f398a2..88356af53664b 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -43,7 +44,7 @@ protected AbstractAutomaticLineEnderCommandHandler( /// /// get ending string if there is one /// - protected abstract string? GetEndingString(Document document, int position, CancellationToken cancellationToken); + protected abstract string? GetEndingString(ParsedDocument document, int position); /// /// do next action @@ -53,22 +54,22 @@ protected AbstractAutomaticLineEnderCommandHandler( /// /// format after inserting ending string /// - protected abstract Document FormatAndApplyBasedOnEndToken(Document document, int position, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken); + protected abstract IList FormatBasedOnEndToken(ParsedDocument document, int position, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken); /// /// special cases where we do not want to do line completion but just fall back to line break and formatting. /// - protected abstract bool TreatAsReturn(Document document, int caretPosition, CancellationToken cancellationToken); + protected abstract bool TreatAsReturn(ParsedDocument document, int caretPosition, CancellationToken cancellationToken); /// /// Add or remove the braces for . /// - protected abstract void ModifySelectedNode(AutomaticLineEnderCommandArgs args, Document document, SyntaxNode selectedNode, bool addBrace, int caretPosition, CancellationToken cancellationToken); + protected abstract void ModifySelectedNode(AutomaticLineEnderCommandArgs args, ParsedDocument document, SyntaxNode selectedNode, bool addBrace, int caretPosition, CancellationToken cancellationToken); /// /// Get the syntax node needs add/remove braces. /// - protected abstract (SyntaxNode selectedNode, bool addBrace)? GetValidNodeToModifyBraces(Document document, int caretPosition, CancellationToken cancellationToken); + protected abstract (SyntaxNode selectedNode, bool addBrace)? GetValidNodeToModifyBraces(ParsedDocument document, int caretPosition, CancellationToken cancellationToken); public CommandState GetCommandState(AutomaticLineEnderCommandArgs args, Func nextHandler) => CommandState.Available; @@ -100,6 +101,7 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle using (context.OperationContext.AddScope(allowCancellation: true, EditorFeaturesResources.Automatically_completing)) { var cancellationToken = context.OperationContext.UserCancellationToken; + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); // caret is not on the subject buffer. nothing we can do var caret = args.TextView.GetCaretPoint(args.SubjectBuffer); @@ -111,7 +113,7 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle var caretPosition = caret.Value; // special cases where we treat this command simply as Return. - if (TreatAsReturn(document, caretPosition, cancellationToken)) + if (TreatAsReturn(parsedDocument, caretPosition, cancellationToken)) { // leave it to the VS editor to handle this command. // VS editor's default implementation of SmartBreakLine is simply BreakLine, which inserts @@ -127,24 +129,24 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle // 2. Append an ending string to the line. (For C#, it is semicolon ';', For VB, it is underline '_') // Check if the node could be used to add/remove brace. - var selectNodeAndOperationKind = GetValidNodeToModifyBraces(document, caretPosition, cancellationToken); + var selectNodeAndOperationKind = GetValidNodeToModifyBraces(parsedDocument, caretPosition, cancellationToken); if (selectNodeAndOperationKind != null) { var (selectedNode, addBrace) = selectNodeAndOperationKind.Value; using var transaction = args.TextView.CreateEditTransaction(EditorFeaturesResources.Automatic_Line_Ender, _undoRegistry, _editorOperationsFactoryService); - ModifySelectedNode(args, document, selectedNode, addBrace, caretPosition, cancellationToken); + ModifySelectedNode(args, parsedDocument, selectedNode, addBrace, caretPosition, cancellationToken); NextAction(operations, nextHandler); transaction.Complete(); return; } // Check if we could find the ending position - var endingInsertionPosition = GetInsertionPositionForEndingString(document, subjectLineWhereCaretIsOn, cancellationToken); + var endingInsertionPosition = GetInsertionPositionForEndingString(parsedDocument, subjectLineWhereCaretIsOn); if (endingInsertionPosition != null) { using var transaction = args.TextView.CreateEditTransaction(EditorFeaturesResources.Automatic_Line_Ender, _undoRegistry, _editorOperationsFactoryService); - var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.Services, explicitFormat: false); - InsertEnding(args.TextView, document, endingInsertionPosition.Value, caretPosition, formattingOptions, cancellationToken); + var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); + InsertEnding(args.TextView, args.SubjectBuffer, parsedDocument, endingInsertionPosition.Value, caretPosition, formattingOptions, cancellationToken); NextAction(operations, nextHandler); transaction.Complete(); return; @@ -160,13 +162,10 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle /// /// return insertion point for the ending string /// - private static int? GetInsertionPositionForEndingString(Document document, ITextSnapshotLine line, CancellationToken cancellationToken) + private static int? GetInsertionPositionForEndingString(ParsedDocument document, ITextSnapshotLine line) { - var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); - var text = root.SyntaxTree.GetText(cancellationToken); - // find last token on the line - var token = root.FindTokenOnLeftOfPosition(line.End); + var token = document.Root.FindTokenOnLeftOfPosition(line.End); if (token.RawKind == 0) return null; @@ -176,6 +175,7 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle return null; // if there is only whitespace, token doesn't need to be on same line + var text = document.Text; if (string.IsNullOrWhiteSpace(text.ToString(TextSpan.FromBounds(token.Span.End, line.End)))) return line.End; @@ -191,7 +191,8 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle /// private void InsertEnding( ITextView textView, - Document document, + ITextBuffer buffer, + ParsedDocument document, int insertPosition, SnapshotPoint caretPosition, SyntaxFormattingOptions formattingOptions, @@ -202,14 +203,17 @@ private void InsertEnding( // 2. Insert ending to the document. var newDocument = document; - var endingString = GetEndingString(document, caretPosition, cancellationToken); + var endingString = GetEndingString(document, caretPosition); if (endingString != null) { - newDocument = document.InsertText(insertPosition, endingString, cancellationToken); + var insertChange = new TextChange(new TextSpan(insertPosition, 0), endingString); + buffer.ApplyChange(insertChange); + newDocument = document.WithChange(insertChange, cancellationToken); } // 3. format the document and apply the changes to the workspace - FormatAndApplyBasedOnEndToken(newDocument, insertPosition, formattingOptions, cancellationToken); + var changes = FormatBasedOnEndToken(newDocument, insertPosition, formattingOptions, cancellationToken); + buffer.ApplyChanges(changes); } } } diff --git a/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs b/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs index 395ecf9154238..ee36c6453301e 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs @@ -4,18 +4,11 @@ #nullable disable -using System; -using System.Collections.Generic; -using System.Threading; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.BraceCompletion; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AutomaticCompletion { @@ -33,72 +26,6 @@ public static CaretPreservingEditTransaction CreateEditTransaction( }; } - public static SyntaxToken FindToken(this ITextSnapshot snapshot, int position, CancellationToken cancellationToken) - { - var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - { - return default; - } - - var root = document.GetSyntaxRootSynchronously(cancellationToken); - return root.FindToken(position, findInsideTrivia: true); - } - - /// - /// insert text to workspace and get updated version of the document - /// - public static Document InsertText(this Document document, int position, string text, CancellationToken cancellationToken = default) - => document.ReplaceText(new TextSpan(position, 0), text, cancellationToken); - - /// - /// replace text to workspace and get updated version of the document - /// - public static Document ReplaceText(this Document document, TextSpan span, string text, CancellationToken cancellationToken) - => document.ApplyTextChange(new TextChange(span, text), cancellationToken); - - /// - /// apply text changes to workspace and get updated version of the document - /// - public static Document ApplyTextChange(this Document document, TextChange textChange, CancellationToken cancellationToken) - => document.ApplyTextChanges(SpecializedCollections.SingletonEnumerable(textChange), cancellationToken); - - /// - /// apply text changes to workspace and get updated version of the document - /// - public static Document ApplyTextChanges(this Document document, IEnumerable textChanges, CancellationToken cancellationToken) - { - // here assumption is that text change are based on current solution - var oldSolution = document.Project.Solution; - var newSolution = oldSolution.UpdateDocument(document.Id, textChanges, cancellationToken); - - if (oldSolution.Workspace.TryApplyChanges(newSolution)) - { - return newSolution.Workspace.CurrentSolution.GetDocument(document.Id); - } - - return document; - } - - /// - /// Update the solution so that the document with the Id has the text changes - /// - public static Solution UpdateDocument(this Solution solution, DocumentId id, IEnumerable textChanges, CancellationToken cancellationToken = default) - { - var oldDocument = solution.GetDocument(id); - var newText = oldDocument.GetTextSynchronously(cancellationToken).WithChanges(textChanges); - return solution.WithDocumentText(id, newText); - } - - public static SnapshotSpan GetSessionSpan(this IBraceCompletionSession session) - { - var snapshot = session.SubjectBuffer.CurrentSnapshot; - var open = session.OpeningPoint.GetPoint(snapshot); - var close = session.ClosingPoint.GetPoint(snapshot); - - return new SnapshotSpan(open, close); - } - public static SnapshotPoint? GetCaretPosition(this IBraceCompletionSession session) => GetCaretPoint(session, session.SubjectBuffer); diff --git a/src/EditorFeatures/Core/BraceMatching/BraceHighlightTag.cs b/src/EditorFeatures/Core/BraceMatching/BraceHighlightTag.cs index 4d29cd5845325..a742c010677e8 100644 --- a/src/EditorFeatures/Core/BraceMatching/BraceHighlightTag.cs +++ b/src/EditorFeatures/Core/BraceMatching/BraceHighlightTag.cs @@ -2,13 +2,13 @@ // 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.VisualStudio.Text.Tagging; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BraceMatching { - internal class BraceHighlightTag : TextMarkerTag + internal sealed class BraceHighlightTag : TextMarkerTag { public static readonly BraceHighlightTag StartTag = new(navigateToStart: true); public static readonly BraceHighlightTag EndTag = new(navigateToStart: false); diff --git a/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs index 0242195975ea9..f7029f978d09d 100644 --- a/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs @@ -174,5 +174,9 @@ private static void AddBraces( context.AddTag(snapshot.GetTagSpan(braces.Value.RightSpan.ToSpan(), BraceHighlightTag.EndTag)); } } + + // Safe to directly compare as BraceHighlightTag uses singleton instances. + protected override bool TagEquals(BraceHighlightTag tag1, BraceHighlightTag tag2) + => tag1 == tag2; } } diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index 3be71fece5c0d..07ef2393725dc 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -20,6 +20,7 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; +using Newtonsoft.Json; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification @@ -124,5 +125,8 @@ protected sealed override Task ProduceTagsAsync( return ClassificationUtilities.ProduceTagsAsync( context, spanToTag, classificationService, _typeMap, classificationOptions, _type, cancellationToken); } + + protected override bool TagEquals(IClassificationTag tag1, IClassificationTag tag2) + => tag1.ClassificationType.Classification == tag2.ClassificationType.Classification; } } diff --git a/src/EditorFeatures/Core/Classification/Semantic/ClassificationUtilities.cs b/src/EditorFeatures/Core/Classification/Semantic/ClassificationUtilities.cs index 2e000f12e457d..bbd5e200fdb9d 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/ClassificationUtilities.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/ClassificationUtilities.cs @@ -179,7 +179,7 @@ await AddClassificationsAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/CodeActions/CodeActionEditHandlerService.cs b/src/EditorFeatures/Core/CodeActions/CodeActionEditHandlerService.cs index 3aff8234b2547..9efa16f33ad28 100644 --- a/src/EditorFeatures/Core/CodeActions/CodeActionEditHandlerService.cs +++ b/src/EditorFeatures/Core/CodeActions/CodeActionEditHandlerService.cs @@ -155,7 +155,7 @@ public async Task ApplyAsync( } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } @@ -181,7 +181,7 @@ public async Task ApplyAsync( } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } transaction.Commit(); diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.cs new file mode 100644 index 0000000000000..33716dd9d00f0 --- /dev/null +++ b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.RoslynErrorTag.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; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + internal partial class AbstractDiagnosticsAdornmentTaggerProvider + { + protected sealed class RoslynErrorTag : ErrorTag, IEquatable + { + private readonly DiagnosticData _data; + + public RoslynErrorTag(string errorType, Workspace workspace, DiagnosticData data) + : base(errorType, CreateToolTipContent(workspace, data)) + { + _data = data; + } + + private static object CreateToolTipContent(Workspace workspace, DiagnosticData diagnostic) + { + Action? navigationAction = null; + string? tooltip = null; + if (workspace != null) + { + var helpLinkUri = diagnostic.GetValidHelpLinkUri(); + if (helpLinkUri != null) + { + navigationAction = new QuickInfoHyperLink(workspace, helpLinkUri).NavigationAction; + tooltip = diagnostic.HelpLink; + } + } + + var diagnosticIdTextRun = navigationAction is null + ? new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id) + : new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id, navigationAction, tooltip); + + return new ContainerElement( + ContainerElementStyle.Wrapped, + new ClassifiedTextElement( + diagnosticIdTextRun, + new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), + new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Message))); + } + + public override bool Equals(object? obj) + => Equals(obj as RoslynErrorTag); + + public bool Equals(RoslynErrorTag? other) + { + return other != null && + this.ErrorType == other.ErrorType && + this._data.GetValidHelpLinkUri() == other._data.GetValidHelpLinkUri() && + this._data.Id == other._data.Id && + this._data.Message == other._data.Message; + } + + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + } + } +} diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs index 729ee70ccb197..3fdb0de8bc686 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -3,19 +3,16 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; namespace Microsoft.CodeAnalysis.Diagnostics { - internal abstract class AbstractDiagnosticsAdornmentTaggerProvider : + internal abstract partial class AbstractDiagnosticsAdornmentTaggerProvider : AbstractDiagnosticsTaggerProvider where TTag : class, ITag { @@ -48,33 +45,6 @@ protected AbstractDiagnosticsAdornmentTaggerProvider( return new TagSpan(adjustedSpan, errorTag); } - protected static object CreateToolTipContent(Workspace workspace, DiagnosticData diagnostic) - { - Action? navigationAction = null; - string? tooltip = null; - if (workspace != null) - { - var helpLinkUri = diagnostic.GetValidHelpLinkUri(); - if (helpLinkUri != null) - { - navigationAction = new QuickInfoHyperLink(workspace, helpLinkUri).NavigationAction; - tooltip = diagnostic.HelpLink; - } - } - - var diagnosticIdTextRun = navigationAction is null - ? new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id) - : new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Id, navigationAction, tooltip); - - return new ContainerElement( - ContainerElementStyle.Wrapped, - new ClassifiedTextElement( - diagnosticIdTextRun, - new ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), - new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), - new ClassifiedTextRun(ClassificationTypeNames.Text, diagnostic.Message))); - } - // By default, tags must have at least length '1' so that they can be visible in the UI layer. protected virtual SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span) => AdjustSnapshotSpan(span, minimumLength: 1, maximumLength: int.MaxValue); diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs index 3f2334bfbd361..d1718319e8237 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs @@ -73,13 +73,6 @@ protected internal override bool IncludeDiagnostic(DiagnosticData data) { if (!data.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary)) { - // All unnecessary code diagnostics should have the 'Unnecessary' custom tag. - // Below assert ensures that we do no report unnecessary code diagnostics that - // want to fade out multiple locations which are encoded as - // additional location indices in the diagnostic's property bag - // without the 'Unnecessary' custom tag. - Debug.Assert(!data.TryGetUnnecessaryLocationIndices(out _)); - return false; } @@ -106,5 +99,8 @@ protected internal override ImmutableArray GetLocationsT // Default to the base implementation for the diagnostic data return base.GetLocationsToTag(diagnosticData); } + + protected override bool TagEquals(ClassificationTag tag1, ClassificationTag tag2) + => tag1.ClassificationType.Classification == tag2.ClassificationType.Classification; } } diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs index c556e2d7577dc..86852fc64410b 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs @@ -15,9 +15,11 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Workspaces; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -73,7 +75,7 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) return null; } - return new ErrorTag(errorType, CreateToolTipContent(workspace, diagnostic)); + return new RoslynErrorTag(errorType, workspace, diagnostic); } private static string? GetErrorTypeFromDiagnostic(DiagnosticData diagnostic) @@ -126,5 +128,12 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) return PredefinedErrorTypeNames.OtherError; } } + + protected override bool TagEquals(IErrorTag tag1, IErrorTag tag2) + { + Contract.ThrowIfFalse(tag1 is RoslynErrorTag); + Contract.ThrowIfFalse(tag2 is RoslynErrorTag); + return tag1.Equals(tag2); + } } } diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index 6d63f95e01c69..2418e14282532 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -17,6 +17,7 @@ using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -56,14 +57,19 @@ protected internal override bool SupportsDignosticMode(DiagnosticMode mode) } protected override IErrorTag CreateTag(Workspace workspace, DiagnosticData diagnostic) - => new ErrorTag( - PredefinedErrorTypeNames.HintedSuggestion, - CreateToolTipContent(workspace, diagnostic)); + => new RoslynErrorTag(PredefinedErrorTypeNames.HintedSuggestion, workspace, diagnostic); protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan) { // We always want suggestion tags to be two characters long. return AdjustSnapshotSpan(snapshotSpan, minimumLength: 2, maximumLength: 2); } + + protected override bool TagEquals(IErrorTag tag1, IErrorTag tag2) + { + Contract.ThrowIfFalse(tag1 is RoslynErrorTag); + Contract.ThrowIfFalse(tag2 is RoslynErrorTag); + return tag1.Equals(tag2); + } } } diff --git a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs index 5b2844f20aa6a..70ccc2fe10f37 100644 --- a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs +++ b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Diagnostics; @@ -101,5 +102,11 @@ protected override async Task ProduceTagsAsync( // Let the context know that this was the span we actually tried to tag. context.SetSpansTagged(ImmutableArray.Create(spanToTag.SnapshotSpan)); } + + protected override bool TagEquals(ITextMarkerTag tag1, ITextMarkerTag tag2) + { + Contract.ThrowIfFalse(tag1 == tag2, "ActiveStatementTag is a supposed to be a singleton"); + return true; + } } } diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs deleted file mode 100644 index a0b961bbc8ead..0000000000000 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueHostWorkspaceEventListener.cs +++ /dev/null @@ -1,55 +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.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.DocumentOpened += WorkspaceDocumentOpened; - } - - public void StopListening(Workspace workspace) - { - workspace.DocumentOpened -= WorkspaceDocumentOpened; - } - - private void WorkspaceDocumentOpened(object? sender, DocumentEventArgs e) - { - if (!DebuggerContractVersionCheck.IsRequiredDebuggerContractVersionAvailable()) - { - return; - } - - WorkspaceDocumentOpenedImpl(e); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void WorkspaceDocumentOpenedImpl(DocumentEventArgs e) - { - var proxy = new RemoteEditAndContinueServiceProxy(e.Document.Project.Solution.Workspace); - _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(e.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/Core/ExternalAccess/VSTypeScript/Api/AbstractVSTypeScriptRequestHandler.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/AbstractVSTypeScriptRequestHandler.cs index 4bed4034d44e6..f2df904d91bce 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/AbstractVSTypeScriptRequestHandler.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/AbstractVSTypeScriptRequestHandler.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; @@ -15,13 +15,13 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; /// /// Request handler type exposed to typescript. /// -internal abstract class AbstractVSTypeScriptRequestHandler : IRequestHandler, IVSTypeScriptRequestHandler +internal abstract class AbstractVSTypeScriptRequestHandler : ILspServiceRequestHandler, IVSTypeScriptRequestHandler, ITextDocumentIdentifierHandler { - bool IRequestHandler.MutatesSolutionState => MutatesSolutionState; + bool IMethodHandler.MutatesSolutionState => MutatesSolutionState; - bool IRequestHandler.RequiresLSPSolution => RequiresLSPSolution; + bool ISolutionRequiredHandler.RequiresLSPSolution => RequiresLSPSolution; - TextDocumentIdentifier? IRequestHandler.GetTextDocumentIdentifier(TRequestType request) + public TextDocumentIdentifier? GetTextDocumentIdentifier(TRequestType request) { var typeScriptIdentifier = GetTypeSciptTextDocumentIdentifier(request); if (typeScriptIdentifier == null) @@ -45,7 +45,7 @@ internal abstract class AbstractVSTypeScriptRequestHandler IRequestHandler.HandleRequestAsync(TRequestType request, RequestContext context, CancellationToken cancellationToken) + public Task HandleRequestAsync(TRequestType request, RequestContext context, CancellationToken cancellationToken) { return HandleRequestAsync(request, new TypeScriptRequestContext(context.Solution, context.Document), cancellationToken); } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs index b29d0bc760426..b578b19d1c746 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs @@ -40,10 +40,9 @@ public VSTypeScriptInProcLanguageClient( [Import(AllowDefault = true)] IVSTypeScriptCapabilitiesProvider? typeScriptCapabilitiesProvider, VSTypeScriptLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, IThreadingContext threadingContext) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext) { _typeScriptCapabilitiesProvider = typeScriptCapabilitiesProvider; } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptLspServiceProvider.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptLspServiceProvider.cs index 6f1db20ab5f37..ff8119570ca13 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptLspServiceProvider.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptLspServiceProvider.cs @@ -17,7 +17,8 @@ internal class VSTypeScriptLspServiceProvider : AbstractLspServiceProvider [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public VSTypeScriptLspServiceProvider( [ImportMany(ProtocolConstants.TypeScriptLanguageContract)] IEnumerable> lspServices, - [ImportMany(ProtocolConstants.TypeScriptLanguageContract)] IEnumerable> lspServiceFactories) : base(lspServices, lspServiceFactories) + [ImportMany(ProtocolConstants.TypeScriptLanguageContract)] IEnumerable> lspServiceFactories) + : base(lspServices, lspServiceFactories) { } } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestDispatcherFactory.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptProjectContextHandler.cs similarity index 57% rename from src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestDispatcherFactory.cs rename to src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptProjectContextHandler.cs index 7041f2561fa86..1c702a7b2b49c 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestDispatcherFactory.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptProjectContextHandler.cs @@ -6,15 +6,17 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; -[ExportLspServiceFactory(typeof(RequestDispatcher), ProtocolConstants.TypeScriptLanguageContract), Shared] -internal class VSTypeScriptRequestDispatcherFactory : RequestDispatcherFactory +[ExportStatelessLspService(typeof(GetTextDocumentWithContextHandler), ProtocolConstants.TypeScriptLanguageContract), Shared] +internal class VSTypeScriptProjectContextHandler : GetTextDocumentWithContextHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VSTypeScriptRequestDispatcherFactory() + public VSTypeScriptProjectContextHandler() { } } diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs index 758c463f5a248..b3e5c4584a95c 100644 --- a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs +++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs @@ -144,7 +144,7 @@ private bool ExecuteSynchronously( } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs b/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs index 33a2ee3c0bf1c..70c685c8cba6a 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs @@ -2,8 +2,13 @@ // 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.Linq; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.InlineHints { @@ -11,13 +16,56 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints /// The simple tag that only holds information regarding the associated parameter name /// for the argument /// - internal class InlineHintDataTag : ITag + internal sealed class InlineHintDataTag : ITag, IEquatable { + private readonly InlineHintsDataTaggerProvider _provider; + + /// + /// The snapshot this tag was created against. + /// + private readonly ITextSnapshot _snapshot; + public readonly InlineHint Hint; - public InlineHintDataTag(InlineHint hint) + public InlineHintDataTag(InlineHintsDataTaggerProvider provider, ITextSnapshot snapshot, InlineHint hint) { + _provider = provider; + _snapshot = snapshot; Hint = hint; } + + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => obj is InlineHintDataTag tag && Equals(tag); + + public bool Equals(InlineHintDataTag? other) + { + if (other is null) + return false; + + // they have to match if they're going to change text. + if (this.Hint.ReplacementTextChange is null != other.Hint.ReplacementTextChange is null) + return false; + + // the text change text has to match. + if (this.Hint.ReplacementTextChange?.NewText != other.Hint.ReplacementTextChange?.NewText) + return false; + + // Ensure both hints are talking about the same snapshot. + if (!_provider.SpanEquals(_snapshot, this.Hint.Span, other._snapshot, other.Hint.Span)) + return false; + + if (this.Hint.ReplacementTextChange != null && + other.Hint.ReplacementTextChange != null && + !_provider.SpanEquals(_snapshot, this.Hint.ReplacementTextChange.Value.Span, other._snapshot, other.Hint.ReplacementTextChange.Value.Span)) + { + return false; + } + + // ensure all the display parts are the same. + return this.Hint.DisplayParts.SequenceEqual(other.Hint.DisplayParts); + } } } diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs index acb7ae8f78090..3b102880170d6 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs @@ -38,7 +38,7 @@ internal class InlineHintsDataTaggerProvider : AsynchronousViewTaggerProvider SpanTrackingMode.EdgeInclusive; + public override SpanTrackingMode SpanTrackingMode => SpanTrackingMode.EdgeInclusive; /// /// We want to make sure that if the user edits the space that the tag exists in that it goes away and they @@ -123,8 +123,11 @@ protected override async Task ProduceTagsAsync( context.AddTag(new TagSpan( hint.Span.ToSnapshotSpan(snapshotSpan.Snapshot), - new InlineHintDataTag(hint))); + new InlineHintDataTag(this, snapshotSpan.Snapshot, hint))); } } + + protected override bool TagEquals(InlineHintDataTag tag1, InlineHintDataTag tag2) + => tag1.Equals(tag2); } } diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs index d43cc94f6b62c..cfb9aa83e78a9 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs @@ -573,7 +573,7 @@ private static async Task> GetTextChangesFromTextDiffere } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs index f32cf33d2c426..295f56e397ba6 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs @@ -859,12 +859,6 @@ await DismissUIAndRollbackEditsAndEndRenameSessionAsync( var changes = _baseSolution.GetChanges(newSolution); var changedDocumentIDs = changes.GetProjectChanges().SelectMany(c => c.GetChangedDocuments()).ToList(); - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - if (!_renameInfo.TryOnBeforeGlobalSymbolRenamed(_workspace, changedDocumentIDs, this.ReplacementText)) - return (NotificationSeverity.Error, EditorFeaturesResources.Rename_operation_was_cancelled_or_is_not_valid); - - using var undoTransaction = _workspace.OpenGlobalUndoTransaction(EditorFeaturesResources.Inline_Rename); - await TaskScheduler.Default; var finalSolution = newSolution.Workspace.CurrentSolution; foreach (var id in changedDocumentIDs) @@ -892,6 +886,12 @@ await DismissUIAndRollbackEditsAndEndRenameSessionAsync( } await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + using var undoTransaction = _workspace.OpenGlobalUndoTransaction(EditorFeaturesResources.Inline_Rename); + + if (!_renameInfo.TryOnBeforeGlobalSymbolRenamed(_workspace, changedDocumentIDs, this.ReplacementText)) + return (NotificationSeverity.Error, EditorFeaturesResources.Rename_operation_was_cancelled_or_is_not_valid); + if (!_workspace.TryApplyChanges(finalSolution)) return (NotificationSeverity.Error, EditorFeaturesResources.Rename_operation_could_not_complete_due_to_external_change_to_workspace); diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index a28b958fd02c3..862776fced7dc 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -264,7 +264,7 @@ private void AddCompletionItems(List list, CancellationToken cancel { // All items passed in should contain a CompletionItemData object in the property bag, // which is guaranteed in `ItemManager.SortCompletionListAsync`. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs b/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs index b168c90b7ee0f..3a69d7fdb3704 100644 --- a/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs +++ b/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs @@ -104,7 +104,7 @@ public async Task GetQuickInfoItemAsync(IAsyncQuickIn } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/IntelliSense/ViewTextSpan.cs b/src/EditorFeatures/Core/IntelliSense/ViewTextSpan.cs index 208c92e541bc9..32483e11292f1 100644 --- a/src/EditorFeatures/Core/IntelliSense/ViewTextSpan.cs +++ b/src/EditorFeatures/Core/IntelliSense/ViewTextSpan.cs @@ -79,7 +79,7 @@ public ViewTextSpan GetSubjectBufferTextSpanInViewBuffer(TextSpan textSpan) } default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/Interactive/Completion/InteractiveCommandCompletionService.cs b/src/EditorFeatures/Core/Interactive/Completion/InteractiveCommandCompletionService.cs index 6d816eb59abc4..592b5e8eaa83e 100644 --- a/src/EditorFeatures/Core/Interactive/Completion/InteractiveCommandCompletionService.cs +++ b/src/EditorFeatures/Core/Interactive/Completion/InteractiveCommandCompletionService.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; namespace Microsoft.CodeAnalysis.Interactive { @@ -15,18 +16,21 @@ internal sealed class InteractiveCommandCompletionService : CompletionService [ExportLanguageServiceFactory(typeof(CompletionService), InteractiveLanguageNames.InteractiveCommand), Shared] internal sealed class Factory : ILanguageServiceFactory { + private readonly IAsynchronousOperationListenerProvider _listenerProvider; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Factory() + public Factory(IAsynchronousOperationListenerProvider listenerProvider) { + _listenerProvider = listenerProvider; } public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => new InteractiveCommandCompletionService(languageServices.LanguageServices.SolutionServices); + => new InteractiveCommandCompletionService(languageServices.LanguageServices.SolutionServices, _listenerProvider); } - private InteractiveCommandCompletionService(SolutionServices services) - : base(services) + private InteractiveCommandCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) + : base(services, listenerProvider) { } diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index 26cad7274c8fa..f13654f4f4e5f 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 FileTextLoader(initializationScriptPath, defaultEncoding: null)); + new WorkspaceFileTextLoader(solution.Services, initializationScriptPath, defaultEncoding: null)); } 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) { @@ -348,7 +352,7 @@ internal async Task ResetAsync(InteractiveHostOptions options) } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs b/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs index a13158791e6fc..7baa2631c81a2 100644 --- a/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs @@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.BraceMatching; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -122,5 +123,11 @@ protected override async Task ProduceTagsAsync( } } } + + protected override bool TagEquals(KeywordHighlightTag tag1, KeywordHighlightTag tag2) + { + Contract.ThrowIfFalse(tag1 == tag2, "KeywordHighlightTag is supposed to be a singleton"); + return true; + } } } diff --git a/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs b/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs index 540772d8002e8..bdedd575b57ca 100644 --- a/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs +++ b/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Highlighting { - internal class KeywordHighlightTag : NavigableHighlightTag + internal sealed class KeywordHighlightTag : NavigableHighlightTag { internal const string TagId = "MarkerFormatDefinition/HighlightedReference"; diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs index cf3016dc52a72..e239b0400282b 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs @@ -8,11 +8,12 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.Threading; @@ -26,17 +27,16 @@ internal abstract partial class AbstractInProcLanguageClient : ILanguageClient, { private readonly IThreadingContext _threadingContext; private readonly ILanguageClientMiddleLayer? _middleLayer; - private readonly ILspLoggerFactory _lspLoggerFactory; + private readonly ILspServiceLoggerFactory _lspLoggerFactory; - private readonly IAsynchronousOperationListenerProvider _listenerProvider; - private readonly AbstractLspServiceProvider _lspServiceProvider; + protected readonly AbstractLspServiceProvider LspServiceProvider; protected readonly IGlobalOptionService GlobalOptions; /// /// Created when is called. /// - private LanguageServerTarget? _languageServer; + private AbstractLanguageServer? _languageServer; /// /// Gets the name of the language client (displayed to the user). @@ -101,14 +101,12 @@ public event AsyncEventHandler? StopAsync { add { } remove { } } public AbstractInProcLanguageClient( AbstractLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, IThreadingContext threadingContext, AbstractLanguageClientMiddleLayer? middleLayer = null) { - _lspServiceProvider = lspServiceProvider; + LspServiceProvider = lspServiceProvider; GlobalOptions = globalOptions; - _listenerProvider = listenerProvider; _lspLoggerFactory = lspLoggerFactory; _threadingContext = threadingContext; _middleLayer = middleLayer; @@ -150,17 +148,16 @@ public AbstractInProcLanguageClient( if (_languageServer is not null) { - Contract.ThrowIfFalse(_languageServer.HasShutdownStarted, "The language server has not yet been asked to shutdown."); - - await _languageServer.DisposeAsync().ConfigureAwait(false); + await _languageServer.WaitForExitAsync().WithCancellation(cancellationToken).ConfigureAwait(false); } var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - _languageServer = (LanguageServerTarget)await CreateAsync( + _languageServer = await CreateAsync( this, serverStream, serverStream, + ServerKind, _lspLoggerFactory, cancellationToken).ConfigureAwait(false); @@ -190,11 +187,12 @@ public Task OnServerInitializedAsync() return Task.CompletedTask; } - internal static async Task CreateAsync( + internal static async Task> CreateAsync( AbstractInProcLanguageClient languageClient, Stream inputStream, Stream outputStream, - ILspLoggerFactory lspLoggerFactory, + WellKnownLspServerKinds serverKind, + ILspServiceLoggerFactory lspLoggerFactory, CancellationToken cancellationToken) { var jsonMessageFormatter = new JsonMessageFormatter(); @@ -212,25 +210,28 @@ internal static async Task CreateAsync( var server = languageClient.Create( jsonRpc, languageClient, + serverKind, logger); jsonRpc.StartListening(); return server; } - public ILanguageServerTarget Create( + public virtual AbstractLanguageServer Create( JsonRpc jsonRpc, ICapabilitiesProvider capabilitiesProvider, - ILspLogger logger) + WellKnownLspServerKinds serverKind, + ILspServiceLogger logger) { - return new LanguageServerTarget( - _lspServiceProvider, + var server = new RoslynLanguageServer( + LspServiceProvider, jsonRpc, capabilitiesProvider, - _listenerProvider, logger, SupportedLanguages, - ServerKind); + serverKind); + + return server; } public abstract ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities); diff --git a/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs index b70399c1de391..b1adf1f7fde0c 100644 --- a/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs @@ -38,11 +38,10 @@ internal class AlwaysActivateInProcLanguageClient : AbstractInProcLanguageClient public AlwaysActivateInProcLanguageClient( CSharpVisualBasicLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, ExperimentalCapabilitiesProvider defaultCapabilitiesProvider, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, IThreadingContext threadingContext) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext) { _experimentalCapabilitiesProvider = defaultCapabilitiesProvider; } diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs index 0a2638c9d4880..b58aa7a27fc03 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs @@ -36,7 +36,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(CodeActionResolveHandler)), Shared] [Method(LSP.Methods.CodeActionResolveName)] - internal class CodeActionResolveHandler : IRequestHandler + internal class CodeActionResolveHandler : ILspServiceDocumentRequestHandler { private readonly ICodeFixService _codeFixService; private readonly ICodeRefactoringService _codeRefactoringService; @@ -57,13 +57,12 @@ public CodeActionResolveHandler( public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.CodeAction request) - => ((JToken)request.Data!).ToObject()?.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) + => ((JToken)request.Data!).ToObject()!.TextDocument; public async Task HandleRequestAsync(LSP.CodeAction codeAction, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); var data = ((JToken)codeAction.Data!).ToObject(); Assumes.Present(data); diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs index 45c8138a0976a..fb35d5dbf39fe 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs @@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(CodeActionsHandler)), Shared] [Method(LSP.Methods.TextDocumentCodeActionName)] - internal class CodeActionsHandler : IRequestHandler + internal class CodeActionsHandler : ILspServiceDocumentRequestHandler { private readonly ICodeFixService _codeFixService; private readonly ICodeRefactoringService _codeRefactoringService; @@ -52,7 +52,7 @@ public CodeActionsHandler( _globalOptions = globalOptions; } - public TextDocumentIdentifier? GetTextDocumentIdentifier(CodeActionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(CodeActionParams request) => request.TextDocument; public async Task HandleRequestAsync(LSP.CodeActionParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs index 63a38b7af59be..9fb862ff58220 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs @@ -61,7 +61,7 @@ public RunCodeActionHandler( public override bool MutatesSolutionState => true; public override bool RequiresLSPSolution => true; - public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.ExecuteCommandParams request) + public override LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.ExecuteCommandParams request) { var runRequest = ((JToken)request.Arguments.Single()).ToObject(); Assumes.Present(runRequest); diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionHandler.cs index 359a1320fbb1b..aa05d1dcdeb23 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionHandler.cs @@ -35,7 +35,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(CompletionHandler)), Shared] [Method(LSP.Methods.TextDocumentCompletionName)] - internal class CompletionHandler : IRequestHandler + internal class CompletionHandler : ILspServiceDocumentRequestHandler { internal const string EditRangeSetting = "editRange"; @@ -60,13 +60,14 @@ public CompletionHandler( lz => CommonCompletionUtilities.GetTriggerCharacters(lz.Value)).ToImmutableHashSet(); } - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.CompletionParams request) => request.TextDocument; + public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CompletionParams request) => request.TextDocument; public async Task HandleRequestAsync(LSP.CompletionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); Contract.ThrowIfNull(context.Solution); + var clientCapabilities = context.GetRequiredClientCapabilities(); // C# and VB share the same LSP language server, and thus share the same default trigger characters. // We need to ensure the trigger character is valid in the document's language. For example, the '{' @@ -102,9 +103,9 @@ public CompletionHandler( }; } - var lspVSClientCapability = context.ClientCapabilities.HasVisualStudioLspCapability() == true; - var snippetsSupported = context.ClientCapabilities.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; - var itemDefaultsSupported = context.ClientCapabilities.TextDocument?.Completion?.CompletionListSetting?.ItemDefaults?.Contains(EditRangeSetting) == true; + var lspVSClientCapability = clientCapabilities.HasVisualStudioLspCapability() == true; + var snippetsSupported = clientCapabilities.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; + var itemDefaultsSupported = clientCapabilities.TextDocument?.Completion?.CompletionListSetting?.ItemDefaults?.Contains(EditRangeSetting) == true; var commitCharactersRuleCache = new Dictionary, string[]>(CommitCharacterArrayComparer.Instance); // We use the first item in the completion list as our comparison point for span @@ -114,7 +115,7 @@ public CompletionHandler( var defaultSpan = completionChange.TextChange.Span; var defaultRange = ProtocolConversions.TextSpanToRange(defaultSpan, documentText); - var supportsCompletionListData = context.ClientCapabilities.HasCompletionListDataCapability(); + var supportsCompletionListData = clientCapabilities.HasCompletionListDataCapability(); var completionResolveData = new CompletionResolveData() { ResultId = resultId, @@ -143,7 +144,7 @@ public CompletionHandler( completionList.Data = completionResolveData; } - if (context.ClientCapabilities.HasCompletionListCommitCharactersCapability()) + if (clientCapabilities.HasCompletionListCommitCharactersCapability()) { PromoteCommonCommitCharactersOntoList(completionList); } diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs index a4b756666a5b0..c5b5a5be08827 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs @@ -3,16 +3,15 @@ // See the LICENSE file in the project root for more information. using System; -using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.Text.Adornments; using Newtonsoft.Json.Linq; using Roslyn.Utilities; @@ -27,8 +26,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// references to VS icon types and classified text runs are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + /// + /// This isn't a because it could return null. + /// [Method(LSP.Methods.TextDocumentCompletionResolveName)] - internal sealed class CompletionResolveHandler : IRequestHandler + internal sealed class CompletionResolveHandler : ILspServiceRequestHandler, ITextDocumentIdentifierHandler { private readonly CompletionListCache _completionListCache; private readonly IGlobalOptionService _globalOptions; @@ -47,8 +49,8 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions, CompletionLi public async Task HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); + var clientCapabilities = context.GetRequiredClientCapabilities(); var completionService = document.Project.Services.GetRequiredService(); @@ -74,7 +76,7 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions, CompletionLi var description = await completionService.GetDescriptionAsync(document, selectedItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false)!; if (description != null) { - var supportsVSExtensions = context.ClientCapabilities.HasVisualStudioLspCapability(); + var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability(); if (supportsVSExtensions) { var vsCompletionItem = (LSP.VSInternalCompletionItem)completionItem; @@ -83,7 +85,7 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions, CompletionLi } else { - var clientSupportsMarkdown = context.ClientCapabilities.TextDocument?.Completion?.CompletionItem?.DocumentationFormat.Contains(LSP.MarkupKind.Markdown) == true; + var clientSupportsMarkdown = clientCapabilities.TextDocument?.Completion?.CompletionItem?.DocumentationFormat.Contains(LSP.MarkupKind.Markdown) == true; completionItem.Documentation = ProtocolConversions.GetDocumentationMarkupContent(description.TaggedParts, document, clientSupportsMarkdown); } } @@ -98,7 +100,7 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions, CompletionLi Contract.ThrowIfTrue(completionItem.InsertText != null); Contract.ThrowIfTrue(completionItem.TextEdit != null); - var snippetsSupported = context.ClientCapabilities.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; + var snippetsSupported = clientCapabilities?.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; completionItem.TextEdit = await GenerateTextEditAsync( document, completionService, selectedItem, snippetsSupported, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Hover/HoverHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Hover/HoverHandler.cs index 8ea9d8e46ccc2..3d5d1eed182b2 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Hover/HoverHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Hover/HoverHandler.cs @@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(HoverHandler)), Shared] [Method(Methods.TextDocumentHoverName)] - internal sealed class HoverHandler : IRequestHandler + internal sealed class HoverHandler : ILspServiceDocumentRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -43,12 +43,12 @@ public HoverHandler(IGlobalOptionService globalOptions) public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); + var clientCapabilities = context.GetRequiredClientCapabilities(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var quickInfoService = document.Project.Services.GetRequiredService(); @@ -61,7 +61,7 @@ public HoverHandler(IGlobalOptionService globalOptions) var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync(info, text, document.Project.Language, document, classificationOptions, context.ClientCapabilities, cancellationToken).ConfigureAwait(false); + return await GetHoverAsync(info, text, document.Project.Language, document, classificationOptions, clientCapabilities, cancellationToken).ConfigureAwait(false); } internal static async Task GetHoverAsync( @@ -69,6 +69,7 @@ public HoverHandler(IGlobalOptionService globalOptions) int position, SymbolDescriptionOptions options, LanguageServices languageServices, + ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { Debug.Assert(semanticModel.Language is LanguageNames.CSharp or LanguageNames.VisualBasic); @@ -83,7 +84,7 @@ public HoverHandler(IGlobalOptionService globalOptions) } var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync(info, text, semanticModel.Language, document: null, classificationOptions: null, clientCapabilities: null, cancellationToken).ConfigureAwait(false); + return await GetHoverAsync(info, text, semanticModel.Language, document: null, classificationOptions: null, clientCapabilities, cancellationToken).ConfigureAwait(false); } private static async Task GetHoverAsync( diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindAllReferencesHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindAllReferencesHandler.cs index 105197ea0d092..b0648a6d4661e 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindAllReferencesHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindAllReferencesHandler.cs @@ -28,7 +28,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(FindAllReferencesHandler)), Shared] [Method(LSP.Methods.TextDocumentReferencesName)] - internal class FindAllReferencesHandler : IRequestHandler + internal class FindAllReferencesHandler : ILspServiceDocumentRequestHandler { private readonly IMetadataAsSourceFileService _metadataAsSourceFileService; private readonly IAsynchronousOperationListener _asyncListener; @@ -49,11 +49,12 @@ public FindAllReferencesHandler( public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(ReferenceParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(ReferenceParams request) => request.TextDocument; public async Task HandleRequestAsync(ReferenceParams referenceParams, RequestContext context, CancellationToken cancellationToken) { - Debug.Assert(context.ClientCapabilities.HasVisualStudioLspCapability()); + var clientCapabilities = context.GetRequiredClientCapabilities(); + Debug.Assert(clientCapabilities.HasVisualStudioLspCapability()); var document = context.Document; var workspace = context.Workspace; diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindImplementationsHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindImplementationsHandler.cs index 8347b40db129f..49031b5aec738 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindImplementationsHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/References/FindImplementationsHandler.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(FindImplementationsHandler)), Shared] [Method(LSP.Methods.TextDocumentImplementationName)] - internal sealed class FindImplementationsHandler : IRequestHandler + internal sealed class FindImplementationsHandler : ILspServiceDocumentRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -31,12 +31,12 @@ public FindImplementationsHandler(IGlobalOptionService globalOptions) public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; + public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); + var clientCapabilities = context.GetRequiredClientCapabilities(); var locations = ArrayBuilder.GetInstance(); @@ -51,7 +51,7 @@ public FindImplementationsHandler(IGlobalOptionService globalOptions) var text = definition.GetClassifiedText(); foreach (var sourceSpan in definition.SourceSpans) { - if (context.ClientCapabilities?.HasVisualStudioLspCapability() == true) + if (clientCapabilities.HasVisualStudioLspCapability() == true) { locations.AddIfNotNull(await ProtocolConversions.DocumentSpanToLocationWithTextAsync(sourceSpan, text, cancellationToken).ConfigureAwait(false)); } diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Rename/RenameHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Rename/RenameHandler.cs index 93dc89a695c92..9e159e945bd30 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Rename/RenameHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Rename/RenameHandler.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(RenameHandler)), Shared] [Method(LSP.Methods.TextDocumentRenameName)] - internal class RenameHandler : IRequestHandler + internal class RenameHandler : ILspServiceDocumentRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -35,7 +35,7 @@ public RenameHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(RenameParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(RenameParams request) => request.TextDocument; public async Task HandleRequestAsync(RenameParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs index 0ecfec5b23e0d..48417f926f066 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs @@ -25,7 +25,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(DocumentSymbolsHandler)), Shared] [Method(Methods.TextDocumentDocumentSymbolName)] - internal class DocumentSymbolsHandler : IRequestHandler + internal class DocumentSymbolsHandler : ILspServiceDocumentRequestHandler { public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; @@ -40,8 +40,8 @@ public DocumentSymbolsHandler() public async Task HandleRequestAsync(RoslynDocumentSymbolParams request, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); + var clientCapabilities = context.GetRequiredClientCapabilities(); var navBarService = document.Project.Services.GetRequiredService(); var navBarItems = await navBarService.GetItemsAsync(document, supportsCodeGeneration: false, forceFrozenPartialSemanticsForCrossProcessOperations: false, cancellationToken).ConfigureAwait(false); @@ -53,7 +53,7 @@ public async Task HandleRequestAsync(RoslynDocumentSymbolParams reques // TODO - Return more than 2 levels of symbols. // https://github.com/dotnet/roslyn/projects/45#card-20033869 using var _ = ArrayBuilder.GetInstance(out var symbols); - if (context.ClientCapabilities?.TextDocument?.DocumentSymbol?.HierarchicalDocumentSymbolSupport == true || request.UseHierarchicalSymbols) + if (clientCapabilities.TextDocument?.DocumentSymbol?.HierarchicalDocumentSymbolSupport == true || request.UseHierarchicalSymbols) { // only top level ones foreach (var item in navBarItems) diff --git a/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs b/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs index 9e18d18560f51..a7d50789c5cd9 100644 --- a/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs +++ b/src/EditorFeatures/Core/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// [ExportCSharpVisualBasicStatelessLspService(typeof(WorkspaceSymbolsHandler)), Shared] [Method(Methods.WorkspaceSymbolName)] - internal class WorkspaceSymbolsHandler : IRequestHandler + internal class WorkspaceSymbolsHandler : ILspServiceRequestHandler { private static readonly IImmutableSet s_supportedKinds = ImmutableHashSet.Create( @@ -55,8 +55,6 @@ public WorkspaceSymbolsHandler( public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(WorkspaceSymbolParams request) => null; - public async Task HandleRequestAsync(WorkspaceSymbolParams request, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Solution); diff --git a/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs index 5578c77b6e6c9..ef4cb1d317b30 100644 --- a/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.LanguageServer.Client; @@ -32,11 +33,10 @@ internal class LiveShareInProcLanguageClient : AbstractInProcLanguageClient public LiveShareInProcLanguageClient( CSharpVisualBasicLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, ExperimentalCapabilitiesProvider experimentalCapabilitiesProvider, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, IThreadingContext threadingContext) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext) { _experimentalCapabilitiesProvider = experimentalCapabilitiesProvider; } diff --git a/src/EditorFeatures/Core/LanguageServer/RazorInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/RazorInProcLanguageClient.cs index 9c3795c7f8a44..2d45da7027459 100644 --- a/src/EditorFeatures/Core/LanguageServer/RazorInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/RazorInProcLanguageClient.cs @@ -49,12 +49,11 @@ internal class RazorInProcLanguageClient : AbstractInProcLanguageClient public RazorInProcLanguageClient( CSharpVisualBasicLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, ExperimentalCapabilitiesProvider experimentalCapabilitiesProvider, IThreadingContext threadingContext, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, [Import(AllowDefault = true)] AbstractLanguageClientMiddleLayer middleLayer) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext, middleLayer) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, middleLayer) { _experimentalCapabilitiesProvider = experimentalCapabilitiesProvider; } diff --git a/src/EditorFeatures/Core/Logging/FunctionIdOptions.cs b/src/EditorFeatures/Core/Logging/FunctionIdOptions.cs index e1e98531b9749..1e7b202684a96 100644 --- a/src/EditorFeatures/Core/Logging/FunctionIdOptions.cs +++ b/src/EditorFeatures/Core/Logging/FunctionIdOptions.cs @@ -20,8 +20,19 @@ internal static class FunctionIdOptions private static Option2 CreateOption(FunctionId id) { - var name = Enum.GetName(typeof(FunctionId), id) ?? throw ExceptionUtilities.UnexpectedValue(id); - + var name = id.ToString(); + + // This local storage location can be set via vsregedit. Which is available on any VS Command Prompt. + // + // To enable logging: + // + // vsregedit set local [hive name] HKCU Roslyn\Internal\Performance\FunctionId [function name] dword 1 + // + // To disable logging + // + // vsregedit delete local [hive name] HKCU Roslyn\Internal\Performance\FunctionId [function name] + // + // If you want to set it for the default hive, use "" as the hive name (i.e. an empty argument) return new(nameof(FunctionIdOptions), name, defaultValue: false, storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\Internal\Performance\FunctionId\" + name)); } diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index 724a6f0e8781e..0da8036810251 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -18,6 +18,9 @@ + + + diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index a73fe83d13a8b..a6c3391eb95d7 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -225,5 +225,9 @@ private static bool IsSupportedContentType(IContentType contentType) return contentType.IsOfType(ContentTypeNames.RoslynContentType) || contentType.IsOfType(ContentTypeNames.XamlContentType); } + + // Safe to directly reference compare as all the NavigableHighlightTag subclasses are singletons. + protected override bool TagEquals(NavigableHighlightTag tag1, NavigableHighlightTag tag2) + => tag1 == tag2; } } diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs index 9a33678068f01..12abb83b795e0 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs @@ -325,7 +325,7 @@ public bool CanInvokeRename( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs index b2ee3ecd34a67..76a2f5ef510f2 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs @@ -136,7 +136,7 @@ public static (CodeAction action, TextSpan renameSpan) TryGetCodeAction( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.General)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextBufferExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextBufferExtensions.cs index ada51f9d5cb6a..8b3106f59f2cb 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextBufferExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextBufferExtensions.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions { diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs new file mode 100644 index 0000000000000..5917289055e8b --- /dev/null +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -0,0 +1,104 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LegacySolutionEvents +{ + [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] + internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener + { + private readonly IGlobalOptionService _globalOptions; + private readonly IThreadingContext _threadingContext; + private readonly AsyncBatchingWorkQueue _eventQueue; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public HostLegacySolutionEventsWorkspaceEventListener( + IGlobalOptionService globalOptions, + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) + { + _globalOptions = globalOptions; + _threadingContext = threadingContext; + _eventQueue = new AsyncBatchingWorkQueue( + DelayTimeSpan.Short, + ProcessWorkspaceChangeEventsAsync, + listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerUnitTesting), + _threadingContext.DisposalToken); + } + + public void StartListening(Workspace workspace, object? serviceOpt) + { + if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + { + workspace.WorkspaceChanged += OnWorkspaceChanged; + _threadingContext.DisposalToken.Register(() => + { + workspace.WorkspaceChanged -= OnWorkspaceChanged; + }); + } + } + + private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) + => _eventQueue.AddWork(e); + + private async ValueTask ProcessWorkspaceChangeEventsAsync(ImmutableSegmentedList events, CancellationToken cancellationToken) + { + if (events.IsEmpty) + return; + + var workspace = events[0].OldSolution.Workspace; + Contract.ThrowIfTrue(events.Any(e => e.OldSolution.Workspace != workspace || e.NewSolution.Workspace != workspace)); + + var client = await RemoteHostClient.TryGetClientAsync(workspace, cancellationToken).ConfigureAwait(false); + + if (client is null) + { + var aggregationService = workspace.Services.GetRequiredService(); + var shouldReport = aggregationService.ShouldReportChanges(workspace.Services.SolutionServices); + if (!shouldReport) + return; + + foreach (var args in events) + await aggregationService.OnWorkspaceChangedAsync(args, cancellationToken).ConfigureAwait(false); + } + else + { + // Notifying OOP of workspace events can be expensive (there may be a lot of them, and they involve + // syncing over entire solution snapshots). As such, do not bother to do this if the remote side says + // that it's not interested in the events. This will happen, for example, when the unittesting + // Test-Explorer window has not been shown yet, and so the unit testing system will not have registered + // an incremental analyzer with us. + var shouldReport = await client.TryInvokeAsync( + (service, cancellationToken) => service.ShouldReportChangesAsync(cancellationToken), + cancellationToken).ConfigureAwait(false); + if (!shouldReport.HasValue || !shouldReport.Value) + return; + + foreach (var args in events) + { + await client.TryInvokeAsync( + args.OldSolution, args.NewSolution, + (service, oldSolutionChecksum, newSolutionChecksum, cancellationToken) => + service.OnWorkspaceChangedAsync(oldSolutionChecksum, newSolutionChecksum, args.Kind, args.ProjectId, args.DocumentId, cancellationToken), + cancellationToken).ConfigureAwait(false); + } + } + } + } +} diff --git a/src/EditorFeatures/Core/Structure/AbstractStructureTaggerProvider.cs b/src/EditorFeatures/Core/Structure/AbstractStructureTaggerProvider.cs index 0e699d2956b4b..1b6c5a6b1bbde 100644 --- a/src/EditorFeatures/Core/Structure/AbstractStructureTaggerProvider.cs +++ b/src/EditorFeatures/Core/Structure/AbstractStructureTaggerProvider.cs @@ -204,7 +204,7 @@ protected sealed override async Task ProduceTagsAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -224,6 +224,13 @@ private void ProcessSpans( } } + protected override bool TagEquals(IStructureTag tag1, IStructureTag tag2) + { + Contract.ThrowIfFalse(tag1 is StructureTag); + Contract.ThrowIfFalse(tag2 is StructureTag); + return tag1.Equals(tag2); + } + internal abstract object? GetCollapsedHintForm(StructureTag structureTag); private static bool s_exceptionReported = false; diff --git a/src/EditorFeatures/Core/Structure/StructureTag.cs b/src/EditorFeatures/Core/Structure/StructureTag.cs index 527fc7d0c5fe4..a074c3179f821 100644 --- a/src/EditorFeatures/Core/Structure/StructureTag.cs +++ b/src/EditorFeatures/Core/Structure/StructureTag.cs @@ -2,15 +2,17 @@ // 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 Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Structure { - internal sealed class StructureTag : IStructureTag + internal sealed class StructureTag : IStructureTag, IEquatable { private readonly AbstractStructureTaggerProvider _tagProvider; @@ -59,6 +61,25 @@ public StructureTag(AbstractStructureTaggerProvider tagProvider, BlockSpan block public bool IsDefaultCollapsed { get; } public bool IsImplementation { get; } + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => Equals(obj as StructureTag); + + public bool Equals(StructureTag? other) + { + return other != null && + this.GuideLineHorizontalAnchorPoint == other.GuideLineHorizontalAnchorPoint && + this.Type == other.Type && + this.IsCollapsible == other.IsCollapsible && + this.IsDefaultCollapsed == other.IsDefaultCollapsed && + this.IsImplementation == other.IsImplementation && + _tagProvider.SpanEquals(this.Snapshot, this.OutliningSpan, other.Snapshot, other.OutliningSpan) && + _tagProvider.SpanEquals(this.Snapshot, this.HeaderSpan, other.Snapshot, other.HeaderSpan) && + _tagProvider.SpanEquals(this.Snapshot, this.GuideLineSpan, other.Snapshot, other.GuideLineSpan); + } + public object? GetCollapsedForm() { return CollapsedText; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs index d46ec6f98deb9..491e9a64163e6 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs @@ -2,11 +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. -#nullable disable - using System.Collections.Generic; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Tagging { @@ -14,11 +12,17 @@ internal abstract partial class AbstractAsynchronousTaggerProvider { private partial class TagSource : IEqualityComparer> { - public bool Equals(ITagSpan x, ITagSpan y) - => x.Span == y.Span && EqualityComparer.Default.Equals(x.Tag, y.Tag); + public bool Equals(ITagSpan? x, ITagSpan? y) + => x != null && y != null && x.Span == y.Span && _dataSource.TagEquals(x.Tag, y.Tag); + /// + /// For the purposes of hashing, just hash spans. This will prevent most collisions. And the rare + /// collision of two tag spans with the same span will be handled by checking if their tags are the same + /// through . This prevents us from having to + /// define a suitable hashing strategy for all our tags. + /// public int GetHashCode(ITagSpan obj) - => Hash.Combine(obj.Span.GetHashCode(), EqualityComparer.Default.GetHashCode(obj.Tag)); + => obj.Span.Span.GetHashCode(); } } } diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index 0a8772b78da40..b1fb2a8971bfa 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -132,7 +132,9 @@ private void RemoveTagsThatIntersectEdit(TextContentChangedEventArgs e) if (!this.CachedTagTrees.TryGetValue(buffer, out var treeForBuffer)) return; - var tagsToRemove = e.Changes.SelectMany(c => treeForBuffer.GetIntersectingSpans(new SnapshotSpan(e.After, c.NewSpan))); + var snapshot = e.After; + + var tagsToRemove = e.Changes.SelectMany(c => treeForBuffer.GetIntersectingSpans(new SnapshotSpan(snapshot, c.NewSpan))); if (!tagsToRemove.Any()) return; @@ -142,8 +144,6 @@ private void RemoveTagsThatIntersectEdit(TextContentChangedEventArgs e) treeForBuffer.SpanTrackingMode, allTags.Except(tagsToRemove, comparer: this)); - var snapshot = e.After; - this.CachedTagTrees = this.CachedTagTrees.SetItem(snapshot.TextBuffer, newTagTree); // Not sure why we are diffing when we already have tagsToRemove. is it due to _tagSpanComparer might return @@ -376,10 +376,9 @@ private IEnumerable> GetNonIntersectingTagSpans(IEnumerable>( - spansToInvalidate.SelectMany(ss => oldTagTree.GetIntersectingSpans(ss))); - - return oldTagTree.GetSpans(snapshot).Except(tagSpansToInvalidate, comparer: this); + return oldTagTree.GetSpans(snapshot).Except( + spansToInvalidate.SelectMany(oldTagTree.GetIntersectingSpans), + comparer: this); } private bool ShouldSkipTagProduction() @@ -399,7 +398,7 @@ private Task ProduceTagsAsync(TaggerContext context, CancellationToken can : _dataSource.ProduceTagsAsync(context, cancellationToken); } - private static Dictionary ProcessNewTagTrees( + private Dictionary ProcessNewTagTrees( ImmutableArray spansToTag, ImmutableDictionary> oldTagTrees, ImmutableDictionary> newTagTrees, @@ -441,7 +440,7 @@ private static Dictionary ProcessNewTagTrees( /// /// Return all the spans that appear in only one of or . /// - private static DiffResult ComputeDifference( + private DiffResult ComputeDifference( ITextSnapshot snapshot, TagSpanIntervalTree latestTree, TagSpanIntervalTree previousTree) @@ -488,7 +487,7 @@ private static DiffResult ComputeDifference( } else { - if (!EqualityComparer.Default.Equals(latest.Tag, previous.Tag)) + if (!_dataSource.TagEquals(latest.Tag, previous.Tag)) added.Add(latestSpan); latest = NextOrNull(latestEnumerator); diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index 6fad7682db16e..e9e26036405f7 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; @@ -58,7 +59,7 @@ internal abstract partial class AbstractAsynchronousTaggerProvider where T /// created for a previous version of a document that are mapped forward by the async /// tagging architecture. This value cannot be . /// - protected virtual SpanTrackingMode SpanTrackingMode => SpanTrackingMode.EdgeExclusive; + public virtual SpanTrackingMode SpanTrackingMode => SpanTrackingMode.EdgeExclusive; /// /// Global options controlling if the tagger should tag or not. @@ -81,6 +82,17 @@ internal abstract partial class AbstractAsynchronousTaggerProvider where T /// protected virtual TaggerDelay AddedTagNotificationDelay => TaggerDelay.NearImmediate; + /// + /// Comparer used to check if two tags are the same. Used so that when new tags are produced, they can be + /// appropriately 'diffed' to determine what changes to actually report in . + /// + protected abstract bool TagEquals(TTag tag1, TTag tag2); + + // Prevent accidental usage of object.Equals instead of TagEquals when comparing tags. + [Obsolete("Did you mean to call TagEquals(TTag tag1, TTag tag2) instead", error: true)] + public static new bool Equals(object objA, object objB) + => throw ExceptionUtilities.Unreachable(); + #if DEBUG public readonly string StackTrace; #endif @@ -218,6 +230,25 @@ await ProduceTagsAsync( protected virtual Task ProduceTagsAsync(TaggerContext context, DocumentSnapshotSpan spanToTag, int? caretPosition, CancellationToken cancellationToken) => Task.CompletedTask; + public bool SpanEquals(ITextSnapshot snapshot1, TextSpan? span1, ITextSnapshot snapshot2, TextSpan? span2) + => SpanEquals(snapshot1, span1?.ToSpan(), snapshot2, span2?.ToSpan()); + + public bool SpanEquals(ITextSnapshot snapshot1, Span? span1, ITextSnapshot snapshot2, Span? span2) + => SpanEquals(span1 is null ? null : new SnapshotSpan(snapshot1, span1.Value), span2 is null ? null : new SnapshotSpan(snapshot2, span2.Value)); + + public bool SpanEquals(SnapshotSpan? span1, SnapshotSpan? span2) + { + if (span1 is null && span2 is null) + return true; + + if (span1 is null || span2 is null) + return false; + + // map one span to the snapshot of the other and see if they match. + span1 = span1.Value.TranslateTo(span2.Value.Snapshot, this.SpanTrackingMode); + return span1.Value.Span == span2.Value.Span; + } + internal TestAccessor GetTestAccessor() => new(this); diff --git a/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs b/src/EditorFeatures/Core/Workspaces/EditorTextFactoryService.cs index 287d60b9f9ead..23a4fcaec4e54 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, 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 = default) + 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/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index eb53a2ff7791d..4b1fcf2ee193a 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -596,7 +596,7 @@ protected static async Task> TestOperationsAsync( if (TestWorkspace.IsWorkspaceElement(expectedText)) { var newSolutionWithLinkedFiles = await newSolution.WithMergedLinkedFileChangesAsync(oldSolution); - await VerifyAgainstWorkspaceDefinitionAsync(expectedText, newSolutionWithLinkedFiles, workspace.ExportProvider); + await VerifyAgainstWorkspaceDefinitionAsync(expectedText, newSolutionWithLinkedFiles, workspace.Composition); return Tuple.Create(oldSolution, newSolution); } @@ -661,9 +661,9 @@ protected static Document GetDocumentToVerify(DocumentId expectedChangedDocument return document; } - private static async Task VerifyAgainstWorkspaceDefinitionAsync(string expectedText, Solution newSolution, ExportProvider exportProvider) + private static async Task VerifyAgainstWorkspaceDefinitionAsync(string expectedText, Solution newSolution, TestComposition composition) { - using (var expectedWorkspace = TestWorkspace.Create(expectedText, exportProvider: exportProvider)) + using (var expectedWorkspace = TestWorkspace.Create(expectedText, composition: composition)) { var expectedSolution = expectedWorkspace.CurrentSolution; Assert.Equal(expectedSolution.Projects.Count(), newSolution.Projects.Count()); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs index 5188bf588aab5..4d6cda31377e8 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -2,18 +2,21 @@ // 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.Collections.Immutable; using System.Net; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing.Verifiers; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Testing; #if !CODE_STYLE using System; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; #endif @@ -70,12 +73,30 @@ public string? EditorConfig set => _sharedState.EditorConfig = value; } + /// + /// The set of code action s offered the user in this exact order. + /// Set this to ensure that a very specific set of actions is offered. + /// + public string[]? ExactActionSetOffered { get; set; } + protected override async Task RunImplAsync(CancellationToken cancellationToken) { _sharedState.Apply(); await base.RunImplAsync(cancellationToken); } + protected override ImmutableArray FilterCodeActions(ImmutableArray actions) + { + var result = base.FilterCodeActions(actions); + + if (ExactActionSetOffered != null) + { + Verify.SequenceEqual(ExactActionSetOffered, result.SelectAsArray(a => a.Title)); + } + + return result; + } + #if !CODE_STYLE protected override AnalyzerOptions GetAnalyzerOptions(Project project) @@ -84,6 +105,23 @@ protected override AnalyzerOptions GetAnalyzerOptions(Project project) protected override CodeRefactoringContext CreateCodeRefactoringContext(Document document, TextSpan span, Action registerRefactoring, CancellationToken cancellationToken) => new CodeRefactoringContext(document, span, (action, textSpan) => registerRefactoring(action), _sharedState.CodeActionOptions, isBlocking: false, cancellationToken); + /// + /// The we want this test to run in. Defaults to if unspecified. + /// + public TestHost TestHost { get; set; } = TestHost.InProcess; + + private static readonly TestComposition s_editorFeaturesOOPComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(TestHost.OutOfProcess); + + protected override Workspace CreateWorkspaceImpl() + { + if (TestHost == TestHost.InProcess) + return base.CreateWorkspaceImpl(); + + var hostServices = s_editorFeaturesOOPComposition.GetHostServices(); + var workspace = new AdhocWorkspace(hostServices); + return workspace; + } + #endif } } diff --git a/src/EditorFeatures/Test/Completion/CompletionServiceTests.cs b/src/EditorFeatures/Test/Completion/CompletionServiceTests.cs index 5cb2fe76b299a..c6b92f3d7317a 100644 --- a/src/EditorFeatures/Test/Completion/CompletionServiceTests.cs +++ b/src/EditorFeatures/Test/Completion/CompletionServiceTests.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -47,7 +48,16 @@ void Method() { var caretPosition = workspace.DocumentWithCursor.CursorPosition ?? throw new InvalidOperationException(); var completions = await completionService.GetCompletionsAsync(document, caretPosition, CompletionOptions.Default, OptionValueSet.Empty); - Assert.False(completions.IsEmpty); + // NuGet providers are not included until it's loaded and cached, this is to avoid potential delays, especially on UI thread. + Assert.Empty(completions.ItemsList); + + // NuGet analyzers for the project will be loaded when this returns + var waiter = workspace.ExportProvider.GetExportedValue().GetWaiter(FeatureAttribute.CompletionSet); + await waiter.ExpeditedWaitAsync(); + + completions = await completionService.GetCompletionsAsync(document, caretPosition, CompletionOptions.Default, OptionValueSet.Empty); + + Assert.NotEmpty(completions.ItemsList); var item = Assert.Single(completions.ItemsList.Where(item => item.ProviderName == typeof(DebugAssertTestCompletionProvider).FullName)); Assert.Equal(nameof(DebugAssertTestCompletionProvider), item.DisplayText); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 9b9254031a931..b8325b3cd59b3 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -16,7 +16,9 @@ using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote.Diagnostics; @@ -390,12 +392,13 @@ public async Task TestSynchronizeWithBuild() // cause analysis var location = Location.Create(document.FilePath, textSpan: default, lineSpan: default); + var properties = ImmutableDictionary.Empty.Add(WellKnownDiagnosticPropertyNames.Origin, WellKnownDiagnosticTags.Build); await service.SynchronizeWithBuildAsync( workspace, ImmutableDictionary>.Empty.Add( document.Project.Id, - ImmutableArray.Create(DiagnosticData.Create(document.Project.Solution, Diagnostic.Create(NoNameAnalyzer.s_syntaxRule, location), document.Project))), + ImmutableArray.Create(DiagnosticData.Create(document.Project.Solution, Diagnostic.Create(NoNameAnalyzer.s_syntaxRule, location, properties), document.Project))), new TaskQueue(service.Listener, TaskScheduler.Default), onBuildCompleted: true, CancellationToken.None); @@ -687,7 +690,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution, false) => 1, (BackgroundAnalysisScope.OpenFiles, true) => 2, (BackgroundAnalysisScope.FullSolution, true) => 4, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.Equal(expectedCount, diagnostics.Count); @@ -868,19 +871,17 @@ void M() sourceGeneratedFiles = Array.Empty(); } - using var workspace = TestWorkspace.CreateCSharp(files, sourceGeneratedFiles, - composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts( - typeof(TestDocumentTrackingService), - typeof(TestWorkspaceConfigurationService))); + var composition = s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts( + typeof(TestDocumentTrackingService)); - var workspaceConfigurationService = workspace.GetService(); - workspaceConfigurationService.Options = new(EnableOpeningSourceGeneratedFiles: true); + using var workspace = new TestWorkspace(composition, configurationOptions: new WorkspaceConfigurationOptions(EnableOpeningSourceGeneratedFiles: true)); workspace.GlobalOptions.SetGlobalOption(new OptionKey(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp), analysisScope); var compilerDiagnosticsScope = analysisScope.ToEquivalentCompilerDiagnosticsScope(); workspace.GlobalOptions.SetGlobalOption(new OptionKey(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp), compilerDiagnosticsScope); + workspace.InitializeDocuments(TestWorkspace.CreateWorkspaceElement(LanguageNames.CSharp, files: files, sourceGeneratedFiles: sourceGeneratedFiles), openDocuments: false); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); var project = workspace.CurrentSolution.Projects.Single(); @@ -1021,7 +1022,7 @@ void M() await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, analyzer.CancellationToken); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (OperationCanceledException ex) when (ex.CancellationToken == analyzer.CancellationToken) { @@ -1119,7 +1120,7 @@ void M() _ = await diagnosticComputer.GetDiagnosticsAsync(analyzerIds, reportSuppressedDiagnostics: false, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: analyzer.CancellationToken); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (OperationCanceledException ex) when (ex.CancellationToken == analyzer.CancellationToken) { @@ -1253,7 +1254,7 @@ private void HandleCallback(Location analysisLocation, Compilation compilation, cancellationToken.ThrowIfCancellationRequested(); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Report diagnostic in the second callback. diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index a2dae42ba27e4..08390bbef8576 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -82,7 +82,7 @@ private static void ValidateHelpLinkForDiagnostic(string diagnosticId, string he return; } - if (helpLinkUri != $"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{diagnosticId.ToLowerInvariant()}") + if (helpLinkUri != $"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{diagnosticId.ToLowerInvariant()}") { Assert.True(false, $"Invalid help link for {diagnosticId}"); } 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..a6c802e7ab9ee 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; @@ -60,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); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index bedd167bba954..a345a908c2687 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: SourceHashAlgorithm.Sha256); + var sourceText = SourceText.From(new MemoryStream(encoding.GetBytesWithPreamble(source.content)), encoding, checksumAlgorithm); return SyntaxFactory.ParseSyntaxTree(sourceText, parseOptions, source.filePath); }); @@ -332,10 +360,13 @@ 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, SourceHashAlgorithm.Sha256); + return SourceText.From(stream, Encoding.UTF8, SourceHashAlgorithms.Default); } private static TextSpan GetSpan(string str, string substr) @@ -360,23 +391,25 @@ 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 = CreateText("class DTO {}"); + 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 { - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, 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(); } } @@ -465,19 +498,20 @@ 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, 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: @@ -490,38 +524,40 @@ 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 FileTextLoader(sourceFileA.Path, encodingA), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileA.Path, encodingA), 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 FileTextLoader(sourceFileB.Path, encodingB), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileB.Path, encodingB), 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 FileTextLoader(sourceFileC.Path, encodingC), + loader: new WorkspaceFileTextLoader(solution.Services, sourceFileC.Path, encodingC), 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 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): + // 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); @@ -529,8 +565,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); @@ -546,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(); @@ -558,14 +594,14 @@ 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); 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); } @@ -585,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); @@ -616,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); @@ -643,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); @@ -658,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); @@ -687,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: @@ -713,16 +749,14 @@ public async Task DesignTimeOnlyDocument_Dynamic() (solution, var document) = AddDefaultTestProject(solution, "class C {}"); + var sourceText = CreateText("class D {}"); 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); @@ -731,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); @@ -758,7 +792,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. var extension = (language == LanguageNames.CSharp) ? ".cs" : ".vb"; var sourceFileName = "a" + extension; - var sourceFilePath = dir.CreateFile(sourceFileName).WriteAllText(source).Path; + var sourceFilePath = dir.CreateFile(sourceFileName).WriteAllText(source, Encoding.UTF8).Path; var designTimeOnlyFileName = "b.g.i" + extension; var designTimeOnlyFilePath = Path.Combine(dir.Path, designTimeOnlyFileName); @@ -776,7 +810,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. solution = solution. AddProject(projectId, "test", "test", language). AddMetadataReferences(projectId, TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40)). - AddDocument(documentId, sourceFileName, SourceText.From(source, Encoding.UTF8), filePath: sourceFilePath); + AddDocument(documentId, sourceFileName, CreateText(source), filePath: sourceFilePath); if (!designTimeOnlyAddedAfterSessionStarts) { @@ -795,7 +829,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. _debuggerService.GetCapabilitiesImpl = () => ImmutableArray.Create("Baseline"); var openDocumentIds = open ? ImmutableArray.Create(designTimeOnlyDocumentId) : 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); if (designTimeOnlyAddedAfterSessionStarts) @@ -814,7 +848,7 @@ public async Task DesignTimeOnlyDocument_Wpf([CombinatorialValues(LanguageNames. EnterBreakState(debuggingSession, activeStatements); // change the source (rude edit): - solution = solution.WithDocumentText(designTimeOnlyDocumentId, SourceText.From(sourceDesignTimeOnly2)); + solution = solution.WithDocumentText(designTimeOnlyDocumentId, CreateText(sourceDesignTimeOnly2)); var designTimeOnlyDocument2 = solution.GetDocument(designTimeOnlyDocumentId); @@ -884,7 +918,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: @@ -938,14 +972,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; @@ -964,7 +998,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: @@ -991,25 +1025,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), 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)); + 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); @@ -1049,15 +1083,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; @@ -1074,7 +1108,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); @@ -1151,10 +1185,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); @@ -1165,7 +1199,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); @@ -1203,7 +1237,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); @@ -1253,7 +1287,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. @@ -1279,6 +1313,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() { @@ -1289,33 +1334,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); } @@ -1343,7 +1397,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); @@ -1401,8 +1455,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( @@ -1414,7 +1468,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); @@ -1480,7 +1534,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(); @@ -1519,10 +1573,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); @@ -1533,7 +1587,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 @@ -1547,7 +1601,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); @@ -1612,7 +1666,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); @@ -1620,7 +1674,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; @@ -1633,7 +1687,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: @@ -1656,7 +1710,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); @@ -1664,7 +1718,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; @@ -1677,7 +1731,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: @@ -1724,7 +1778,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: @@ -1764,7 +1818,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: @@ -1824,7 +1878,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)); @@ -1916,10 +1970,10 @@ 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", 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, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, pathX, CancellationToken.None)); @@ -1947,10 +2001,10 @@ 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, CreateText("xxx")), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx")), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "1") })), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.False(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); Assert.False(await EditSession.HasChangesAsync(oldSolution, solution, pathX, CancellationToken.None)); @@ -1974,10 +2028,10 @@ 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, CreateText("xxx-changed")), + DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx-changed")), DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText(new[] { ("x", "2") })), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, pathX, CancellationToken.None)); @@ -2001,7 +2055,7 @@ public async Task HasChanges_Documents(DocumentKind documentKind) DocumentKind.Source => solution.RemoveDocument(documentId), DocumentKind.Additional => solution.RemoveAdditionalDocument(documentId), DocumentKind.AnalyzerConfig => solution.RemoveAnalyzerConfigDocument(documentId), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, pathX, CancellationToken.None)); @@ -2027,8 +2081,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 }); @@ -2040,8 +2094,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( @@ -2061,7 +2115,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", CreateText(sourceB2), filePath: sourceFileB.Path); solution = documentB2.Project.Solution; @@ -2094,7 +2148,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, CreateText(sourceB3)); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); @@ -2127,7 +2181,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); @@ -2239,7 +2293,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); @@ -2275,7 +2329,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 @@ -2308,7 +2362,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); @@ -2366,7 +2420,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); @@ -2374,27 +2428,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. @@ -2409,7 +2474,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); @@ -2443,7 +2508,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); @@ -2451,7 +2516,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; @@ -2465,7 +2530,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 @@ -2480,16 +2545,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); @@ -2511,7 +2576,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); @@ -2531,7 +2596,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( @@ -2573,7 +2638,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); @@ -2581,7 +2646,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; @@ -2604,7 +2669,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); @@ -2640,7 +2705,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); @@ -2781,7 +2846,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: @@ -2834,7 +2899,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); @@ -2896,8 +2961,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); @@ -2944,7 +3009,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); @@ -3004,7 +3069,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); @@ -3052,7 +3117,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); @@ -3100,7 +3165,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); @@ -3226,7 +3291,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 @@ -3296,8 +3361,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); @@ -3344,8 +3409,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); @@ -3416,7 +3481,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)); @@ -3449,7 +3514,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)); @@ -3485,7 +3550,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); @@ -3578,7 +3643,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); @@ -3645,17 +3710,14 @@ 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 = CreateText("dummy1"); 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); @@ -3680,7 +3742,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()); @@ -3839,7 +3901,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); @@ -3937,7 +3999,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); @@ -4019,7 +4081,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); @@ -4031,13 +4093,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); @@ -4105,7 +4167,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); @@ -4125,7 +4187,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); @@ -4161,7 +4223,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); @@ -4238,7 +4300,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 @@ -4268,7 +4330,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)); @@ -4347,7 +4409,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); @@ -4400,10 +4462,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). @@ -4415,7 +4477,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 => @@ -4423,19 +4485,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); @@ -4485,8 +4548,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); @@ -4501,7 +4564,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")); @@ -4517,7 +4580,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); @@ -4525,7 +4588,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( @@ -4535,7 +4598,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); @@ -4553,8 +4616,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); @@ -4568,7 +4631,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); @@ -4584,13 +4647,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); @@ -4601,7 +4664,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); @@ -4609,5 +4672,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/Test/FindSymbols/SymbolTreeInfoTests.cs b/src/EditorFeatures/Test/FindSymbols/SymbolTreeInfoTests.cs new file mode 100644 index 0000000000000..e99c459743acb --- /dev/null +++ b/src/EditorFeatures/Test/FindSymbols/SymbolTreeInfoTests.cs @@ -0,0 +1,109 @@ +// 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.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.FindSymbols +{ + [UseExportProvider] + public class SymbolTreeInfoTests + { + [Fact] + public async Task TestSymbolTreeInfoForMetadataWithDifferentProperties1() + { + using var workspace = TestWorkspace.CreateCSharp(""); + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var reference1 = (PortableExecutableReference)project.MetadataReferences.First(); + var reference2 = reference1.WithAliases(new[] { "Alias" }); + + var info1 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference1, checksum: null, CancellationToken.None); + + var info2 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference2, checksum: null, CancellationToken.None); + + Assert.NotEqual(info1.Checksum, info2.Checksum); + Assert.Equal(info1.Checksum, SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference1, CancellationToken.None)); + Assert.Equal(info2.Checksum, SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference2, CancellationToken.None)); + } + + [Fact] + public async Task TestSymbolTreeInfoForMetadataWithDifferentProperties2() + { + using var workspace = TestWorkspace.CreateCSharp(""); + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var reference1 = (PortableExecutableReference)project.MetadataReferences.First(); + var reference2 = reference1.WithAliases(new[] { "Alias" }); + + var checksum1 = SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference1, CancellationToken.None); + var info1 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference1, checksum1, CancellationToken.None); + + var checksum2 = SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference2, CancellationToken.None); + var info2 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference2, checksum2, CancellationToken.None); + + Assert.NotEqual(info1.Checksum, info2.Checksum); + Assert.Equal(info1.Checksum, checksum1); + Assert.Equal(info2.Checksum, checksum2); + } + + [Fact] + public async Task TestSymbolTreeInfoForMetadataWithDifferentProperties3() + { + using var workspace = TestWorkspace.CreateCSharp(""); + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var reference1 = (PortableExecutableReference)project.MetadataReferences.First(); + var reference2 = reference1.WithAliases(new[] { "Alias" }); + + var checksum1 = SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference1, CancellationToken.None); + var info1 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference1, checksum1, CancellationToken.None); + + var info2 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference2, checksum: null, CancellationToken.None); + + Assert.NotEqual(info1.Checksum, info2.Checksum); + Assert.Equal(info1.Checksum, checksum1); + Assert.Equal(info2.Checksum, SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference2, CancellationToken.None)); + } + + [Fact] + public async Task TestSymbolTreeInfoForMetadataWithDifferentProperties4() + { + using var workspace = TestWorkspace.CreateCSharp(""); + var solution = workspace.CurrentSolution; + var project = solution.Projects.Single(); + + var reference1 = (PortableExecutableReference)project.MetadataReferences.First(); + var reference2 = reference1.WithAliases(new[] { "Alias" }); + + var info1 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference1, checksum: null, CancellationToken.None); + + var checksum2 = SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference2, CancellationToken.None); + var info2 = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + solution, reference2, checksum2, CancellationToken.None); + + Assert.NotEqual(info1.Checksum, info2.Checksum); + Assert.Equal(info1.Checksum, SymbolTreeInfo.GetMetadataChecksum(solution.Services, reference1, CancellationToken.None)); + Assert.Equal(info2.Checksum, checksum2); + } + } +} diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index fae66fa2619a0..5e7e6ad7ac151 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -25,6 +25,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.MetadataAsSource { + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.MetadataAsSource)] public partial class MetadataAsSourceTests : AbstractMetadataAsSourceTests { @@ -42,7 +43,7 @@ private static string ToLanguageName(OriginatingProjectLanguage language) _ => throw ExceptionUtilities.UnexpectedValue(language), }; - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestClass(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C {}"; @@ -95,13 +96,13 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546241, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546241"), CombinatorialData] + [WpfTheory, WorkItem(546241, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546241"), CombinatorialData] public async Task TestInterface(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public interface I {}"; @@ -152,13 +153,13 @@ public interface [|I|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestConstructor(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C {}"; @@ -211,13 +212,13 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestMethod(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { public void Goo() {} }"; @@ -280,13 +281,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestField(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { public string S; }"; @@ -345,13 +346,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546240, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546240"), CombinatorialData] + [WpfTheory, WorkItem(546240, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546240"), CombinatorialData] public async Task TestProperty(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { public string S { get; protected set; } }"; @@ -410,13 +411,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546291, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546291"), CombinatorialData] + [WpfTheory, WorkItem(546291, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546291"), CombinatorialData] [WorkItem(546194, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546194")] public async Task TestEvent(OriginatingProjectLanguage language, bool signaturesOnly) { @@ -484,13 +485,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestNestedType(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { protected class D { } }"; @@ -558,13 +559,13 @@ protected class [|D|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546195, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546195"), WorkItem(546269, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546269"), CombinatorialData] + [WpfTheory, WorkItem(546195, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546195"), WorkItem(546269, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546269"), CombinatorialData] public async Task TestEnum(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public enum E { A, B, C }"; @@ -627,13 +628,13 @@ public enum [|E|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546195, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546195"), WorkItem(546269, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546269"), CombinatorialData] + [WpfTheory, WorkItem(546195, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546195"), WorkItem(546269, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546269"), CombinatorialData] public async Task TestEnumFromField(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public enum E { A, B, C }"; @@ -696,13 +697,13 @@ public enum E {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546273, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546273"), CombinatorialData] + [WpfTheory, WorkItem(546273, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546273"), CombinatorialData] public async Task TestEnumWithUnderlyingType(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public enum E : short { A = 0, B = 1, C = 2 }"; @@ -765,13 +766,13 @@ public enum E : short {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(650741, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/650741"), CombinatorialData] + [WpfTheory, WorkItem(650741, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/650741"), CombinatorialData] public async Task TestEnumWithOverflowingUnderlyingType(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public enum E : ulong { A = 9223372036854775808 }"; @@ -826,13 +827,13 @@ public enum E : ulong {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestEnumWithDifferentValues(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public enum E : short { A = 1, B = 2, C = 3 }"; @@ -895,13 +896,13 @@ public enum E : short {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198"), CombinatorialData] + [WpfTheory, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198"), CombinatorialData] public async Task TestTypeInNamespace(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "namespace N { public class C {} }"; @@ -965,13 +966,13 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Fact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [WpfFact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] public async Task TestTypeInFileScopedNamespace1() { var metadataSource = "namespace N { public class C {} }"; @@ -993,7 +994,7 @@ public class [|C|] fileScopedNamespaces: true); } - [Fact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [WpfFact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] public async Task TestTypeInFileScopedNamespace2() { var metadataSource = "namespace N { public class C {} }"; @@ -1015,7 +1016,7 @@ public class [|C|] }}", fileScopedNamespaces: true); } - [Fact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [WpfFact, WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] public async Task TestTypeInFileScopedNamespace3() { var metadataSource = "namespace N { public class C {} }"; @@ -1037,7 +1038,7 @@ public class [|C|] }}"); } - [Theory, WorkItem(546223, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546223"), CombinatorialData] + [WpfTheory, WorkItem(546223, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546223"), CombinatorialData] public async Task TestInlineConstant(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @"public class C { public const string S = ""Hello mas""; }"; @@ -1096,13 +1097,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546221, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546221"), CombinatorialData] + [WpfTheory, WorkItem(546221, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546221"), CombinatorialData] public async Task TestInlineTypeOf(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -1169,13 +1170,13 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546231, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546231"), CombinatorialData] + [WpfTheory, WorkItem(546231, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546231"), CombinatorialData] public async Task TestNoDefaultConstructorInStructs(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public struct S {}"; @@ -1232,13 +1233,13 @@ public struct [|S|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestReferenceDefinedType(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { public static C Create() { return new C(); } }"; @@ -1303,13 +1304,13 @@ public static C Create() {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546227, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546227"), CombinatorialData] + [WpfTheory, WorkItem(546227, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546227"), CombinatorialData] public async Task TestGenericType(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class G { public SomeType S; }"; @@ -1368,13 +1369,13 @@ public class [|G|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] [WorkItem(38916, "https://github.com/dotnet/roslyn/issues/38916")] public async Task TestParameterAttributes(OriginatingProjectLanguage language, bool signaturesOnly) { @@ -1445,13 +1446,13 @@ public void Method([My] T x, [My] T y) {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] [WorkItem(38916, "https://github.com/dotnet/roslyn/issues/38916")] public async Task TestGenericWithNullableReferenceTypes(OriginatingProjectLanguage language, bool signaturesOnly) { @@ -1480,8 +1481,6 @@ public interface [|C|] ' {CodeAnalysisResources.InMemoryAssembly} #End Region - - Public Interface [|C|](Of T) Function Equals( other As T) As Boolean End Interface", @@ -1517,13 +1516,13 @@ public interface [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546227, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546227"), CombinatorialData] + [WpfTheory, WorkItem(546227, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546227"), CombinatorialData] public async Task TestGenericDelegate(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = "public class C { public delegate void D(SomeType s); }"; @@ -1581,13 +1580,13 @@ public class C {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(546200, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546200"), CombinatorialData] + [WpfTheory, WorkItem(546200, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546200"), CombinatorialData] public async Task TestAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -1665,27 +1664,27 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Fact] + [WpfFact] public async Task TestSymbolIdMatchesMetadata() { await TestSymbolIdMatchesMetadataAsync(LanguageNames.CSharp); await TestSymbolIdMatchesMetadataAsync(LanguageNames.VisualBasic); } - [Fact] + [WpfFact] public async Task TestNotReusedOnAssemblyDiffers() { await TestNotReusedOnAssemblyDiffersAsync(LanguageNames.CSharp); await TestNotReusedOnAssemblyDiffersAsync(LanguageNames.VisualBasic); } - [Fact] + [WpfFact] public async Task TestThrowsOnGenerateNamespace() { var namespaceSymbol = CodeGenerationSymbolFactory.CreateNamespaceSymbol("Outerspace"); @@ -1697,7 +1696,7 @@ await Assert.ThrowsAsync(async () => }); } - [Fact] + [WpfFact] public async Task TestReuseGenerateMemberOfGeneratedType() { var metadataSource = "public class C { public bool Is; }"; @@ -1708,7 +1707,7 @@ public async Task TestReuseGenerateMemberOfGeneratedType() TestContext.VerifyDocumentReused(a, b); } - [Fact] + [WpfFact] public async Task TestReuseRepeatGeneration() { using var context = TestContext.Create(); @@ -1717,7 +1716,7 @@ public async Task TestReuseRepeatGeneration() TestContext.VerifyDocumentReused(a, b); } - [Fact] + [WpfFact] public async Task TestWorkspaceContextHasReasonableProjectName() { using var context = TestContext.Create(); @@ -1729,7 +1728,7 @@ public async Task TestWorkspaceContextHasReasonableProjectName() Assert.Equal("mscorlib", openedDocument.Project.Name); } - [Fact] + [WpfFact] public async Task TestReuseGenerateFromDifferentProject() { using var context = TestContext.Create(); @@ -1743,7 +1742,7 @@ public async Task TestReuseGenerateFromDifferentProject() TestContext.VerifyDocumentReused(a, b); } - [Fact] + [WpfFact] public async Task TestNotReusedGeneratingForDifferentLanguage() { using var context = TestContext.Create(LanguageNames.CSharp); @@ -1757,7 +1756,7 @@ public async Task TestNotReusedGeneratingForDifferentLanguage() TestContext.VerifyDocumentNotReused(a, b); } - [Fact, WorkItem(546311, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546311")] + [WpfFact, WorkItem(546311, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546311")] public async Task FormatMetadataAsSource() { using var context = TestContext.Create(LanguageNames.CSharp); @@ -1766,7 +1765,7 @@ public async Task FormatMetadataAsSource() await Formatter.FormatAsync(document, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); } - [Fact, WorkItem(530829, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")] + [WpfFact, WorkItem(530829, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")] public async Task IndexedProperty() { var metadataSource = @" @@ -1795,7 +1794,7 @@ public class C await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } - [Fact, WorkItem(566688, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/566688")] + [WpfFact, WorkItem(566688, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/566688")] public async Task AttributeReferencingInternalNestedType() { var metadataSource = @"using System; @@ -1825,7 +1824,7 @@ public class [|C|] await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } - [Theory, WorkItem(530978, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530978"), CombinatorialData] + [WpfTheory, WorkItem(530978, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530978"), CombinatorialData] public async Task TestAttributesOnMembers(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @"using System; @@ -2126,13 +2125,13 @@ public void method2([CallerMemberName] string name = """") {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(530923, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530923"), CombinatorialData] + [WpfTheory, WorkItem(530923, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530923"), CombinatorialData] public async Task TestEmptyLineBetweenMembers(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @"using System; @@ -2377,13 +2376,13 @@ public void method2([CallerMemberName] string name = """") {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(728644, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/728644"), CombinatorialData] + [WpfTheory, WorkItem(728644, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/728644"), CombinatorialData] public async Task TestEmptyLineBetweenMembers2(OriginatingProjectLanguage language, bool signaturesOnly) { var source = @" @@ -2491,13 +2490,13 @@ public interface [|IGoo|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "System.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(source, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly, includeXmlDocComments: true); } - [Theory, WorkItem(679114, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/679114"), WorkItem(715013, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/715013"), CombinatorialData] + [WpfTheory, WorkItem(679114, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/679114"), WorkItem(715013, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/715013"), CombinatorialData] public async Task TestDefaultValueEnum(OriginatingProjectLanguage language, bool signaturesOnly) { var source = @" @@ -2575,13 +2574,13 @@ public void goo(FileOptions options = FileOptions.None) {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(source, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(651261, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651261"), CombinatorialData] + [WpfTheory, WorkItem(651261, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651261"), CombinatorialData] public async Task TestNullAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { var source = @" @@ -2663,13 +2662,13 @@ public TestAttribute(int[] i) {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(source, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Fact, WorkItem(897006, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] + [WpfFact, WorkItem(897006, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodCS() { var metadata = @"using System; @@ -2704,7 +2703,7 @@ public static class ObjectExtensions TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact, WorkItem(897006, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] + [WpfFact, WorkItem(897006, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodVB() { var metadata = @"Imports System.Runtime.CompilerServices @@ -2746,7 +2745,7 @@ End Module TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestIndexersAndOperators(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @"public class Program @@ -2861,13 +2860,13 @@ public int this[int x] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(15387, "https://github.com/dotnet/roslyn/issues/15387"), CombinatorialData] + [WpfTheory, WorkItem(15387, "https://github.com/dotnet/roslyn/issues/15387"), CombinatorialData] public async Task TestComImport1(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -2967,13 +2966,13 @@ public interface [|IComImport|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, CombinatorialData] + [WpfTheory, CombinatorialData] public async Task TestOptionalParameterWithDefaultLiteral(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3049,20 +3048,20 @@ public class [|C|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var languageVersion = language switch { OriginatingProjectLanguage.CSharp => "7.1", OriginatingProjectLanguage.VisualBasic => "15.5", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly, languageVersion: languageVersion); } - [Theory, WorkItem(446567, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=446567"), CombinatorialData] + [WpfTheory, WorkItem(446567, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=446567"), CombinatorialData] public async Task TestDocCommentsWithUnixNewLine(OriginatingProjectLanguage language, bool signaturesOnly) { var source = @" @@ -3170,13 +3169,13 @@ public interface [|IGoo|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "System.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(source, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly, includeXmlDocComments: true); } - [Fact] + [WpfFact] public async Task TestUnmanagedCSharpConstraint_Type() { var metadata = @"using System; @@ -3211,7 +3210,7 @@ public class [|TestType|] where T : unmanaged TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestUnmanagedCSharpConstraint_Method() { var metadata = @"using System; @@ -3251,7 +3250,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestUnmanagedCSharpConstraint_Delegate() { var metadata = @"using System; @@ -3280,7 +3279,7 @@ class C TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValue() { var source = @" @@ -3294,7 +3293,7 @@ class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.CSharp, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValueVB() { var source = @" @@ -3307,7 +3306,7 @@ Class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.VisualBasic, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValue() { var source = @" @@ -3321,7 +3320,7 @@ class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.CSharp, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValueVB() { var source = @" @@ -3334,7 +3333,7 @@ Class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.VisualBasic, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValue() { var source = @" @@ -3348,7 +3347,7 @@ class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.CSharp, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValueVB() { var source = @" @@ -3361,7 +3360,7 @@ Class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.VisualBasic, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValue() { var source = @" @@ -3375,7 +3374,7 @@ class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.CSharp, expected); } - [Fact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] + [WpfFact, WorkItem(29786, "https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValueVB() { var source = @" @@ -3388,7 +3387,7 @@ Class C await GenerateAndVerifySourceLineAsync(source, LanguageNames.VisualBasic, expected); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3450,13 +3449,13 @@ public readonly struct [|S|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3516,13 +3515,13 @@ public struct [|S|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3586,13 +3585,13 @@ public ref struct [|S|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3656,13 +3655,13 @@ public readonly ref struct [|S|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3733,13 +3732,13 @@ public struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod_InReadOnlyStruct(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3811,13 +3810,13 @@ public readonly struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3877,13 +3876,13 @@ public struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly_CSharp7_3(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -3943,20 +3942,20 @@ public struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var metadataLanguageVersion = language switch { OriginatingProjectLanguage.CSharp => "7.3", OriginatingProjectLanguage.VisualBasic => "Preview", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly, metadataLanguageVersion: metadataLanguageVersion); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4016,13 +4015,13 @@ public struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4084,13 +4083,13 @@ public readonly struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4174,13 +4173,13 @@ readonly get {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_Get_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4264,13 +4263,13 @@ readonly set {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4354,13 +4353,13 @@ public readonly int [|P|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4432,13 +4431,13 @@ public struct S {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4528,13 +4527,13 @@ readonly get {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4622,13 +4621,13 @@ public readonly event Action [|E|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Theory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] + [WpfTheory, WorkItem(34650, "https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { var metadataSource = @" @@ -4717,13 +4716,13 @@ public event Action [|E|] {string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} {string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} #endif", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, ToLanguageName(language), expected, signaturesOnly: signaturesOnly); } - [Fact] + [WpfFact] public async Task TestNotNullCSharpConstraint_Type() { var metadata = @"using System; @@ -4760,7 +4759,7 @@ public class [|TestType|] where T : notnull TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNotNullCSharpConstraint_Method() { var metadata = @"using System; @@ -4802,7 +4801,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNotNullCSharpConstraint_Delegate() { var metadata = @"using System; @@ -4833,7 +4832,7 @@ class C TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable1() { var metadata = @" @@ -4891,7 +4890,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable2() { var metadata = @" @@ -4946,7 +4945,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable3() { var metadata = @" @@ -5009,7 +5008,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable4() { var metadata = @" @@ -5059,7 +5058,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable5() { var metadata = @" @@ -5110,7 +5109,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable6() { var metadata = @" @@ -5158,7 +5157,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable7() { var metadata = @" @@ -5206,7 +5205,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable8() { var metadata = @" @@ -5252,7 +5251,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable9() { var metadata = @" @@ -5298,7 +5297,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable10() { var metadata = @" @@ -5344,7 +5343,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable11() { var metadata = @" @@ -5388,7 +5387,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable12() { var metadata = @" @@ -5452,7 +5451,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestNullableEnableDisable13() { var metadata = @" @@ -5526,7 +5525,7 @@ public class Nested TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact] + [WpfFact] public async Task TestDynamic1() { var metadata = @" @@ -5570,7 +5569,7 @@ public class TestType TestContext.VerifyResult(metadataAsSourceFile, expected); } - [Fact, WorkItem(22431, "https://github.com/dotnet/roslyn/issues/22431")] + [WpfFact, WorkItem(22431, "https://github.com/dotnet/roslyn/issues/22431")] public async Task TestCDATAComment() { var source = @" diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 6afbf99c5714d..17a3ff16012cf 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -248,7 +248,7 @@ public async Task TestPreviewDiagnosticTaggerInPreviewPane() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestPreviewWorkspaceDoesNotLeakSolution() { // Verify that analyzer execution doesn't leak solution instances from the preview workspace. diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs index b0698e67f4b3e..83c66565a7fc3 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs @@ -217,7 +217,7 @@ private async Task WaitForAsyncOperationsAsync() await provider.WaitAllDispatcherOperationAndTasksAsync( Workspace, FeatureAttribute.RenameTracking, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.Workspace, FeatureAttribute.EventHookup); } diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index e5aeef6389a2a..3b74a42e2ae1b 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -824,8 +824,8 @@ internal async Task Document_Cancellation(BackgroundAnalysisScope analysisScope, var listenerProvider = GetListenerProvider(workspace.ExportProvider); // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) @@ -879,8 +879,8 @@ internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope var listenerProvider = GetListenerProvider(workspace.ExportProvider); // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) @@ -979,14 +979,14 @@ internal async Task Document_ActiveDocumentChanged(BackgroundAnalysisScope analy { (BackgroundAnalysisScope.ActiveFile, _) => 1, (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution or BackgroundAnalysisScope.None, _) => 0, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var expectedDocumentEvents = (analysisScope, hasActiveDocumentBefore) switch { (BackgroundAnalysisScope.ActiveFile, _) => 1, (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution or BackgroundAnalysisScope.None, _) => 0, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; // Switch to another active source document and verify expected document analysis callbacks @@ -1457,8 +1457,8 @@ public async Task FileFromSameProjectTogetherTest() var listenerProvider = GetListenerProvider(workspace.ExportProvider); // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); // we want to test order items processed by solution crawler. // but since everything async, lazy and cancellable, order is not 100% deterministic. an item might @@ -1490,7 +1490,7 @@ public async Task FileFromSameProjectTogetherTest() await workspaceWaiter.ExpeditedWaitAsync(); // now wait for semantic processor to finish - var crawlerListener = (AsynchronousOperationListener)GetListenerProvider(workspace.ExportProvider).GetListener(FeatureAttribute.SolutionCrawler); + var crawlerListener = (AsynchronousOperationListener)GetListenerProvider(workspace.ExportProvider).GetListener(FeatureAttribute.SolutionCrawlerLegacy); // first, wait for first work to be queued. // @@ -1603,7 +1603,7 @@ private static async Task WaitWaiterAsync(ExportProvider provider) var workspaceWaiter = GetListenerProvider(provider).GetWaiter(FeatureAttribute.Workspace); await workspaceWaiter.ExpeditedWaitAsync(); - var solutionCrawlerWaiter = GetListenerProvider(provider).GetWaiter(FeatureAttribute.SolutionCrawler); + var solutionCrawlerWaiter = GetListenerProvider(provider).GetWaiter(FeatureAttribute.SolutionCrawlerLegacy); await solutionCrawlerWaiter.ExpeditedWaitAsync(); } @@ -1688,7 +1688,7 @@ public WorkCoordinatorWorkspace(string workspaceKind = null, bool disablePartial : base(composition: incrementalAnalyzer is null ? s_composition : s_composition.AddParts(incrementalAnalyzer), workspaceKind: workspaceKind, disablePartialSolutions: disablePartialSolutions) { _workspaceWaiter = GetListenerProvider(ExportProvider).GetWaiter(FeatureAttribute.Workspace); - _solutionCrawlerWaiter = GetListenerProvider(ExportProvider).GetWaiter(FeatureAttribute.SolutionCrawler); + _solutionCrawlerWaiter = GetListenerProvider(ExportProvider).GetWaiter(FeatureAttribute.SolutionCrawlerLegacy); Assert.False(_workspaceWaiter.HasPendingWork); Assert.False(_solutionCrawlerWaiter.HasPendingWork); @@ -2026,7 +2026,7 @@ private string GetListenerTrace(ExportProvider provider) sb.AppendLine("workspace"); sb.AppendLine(workspaceWaiter.Trace()); - var solutionCrawlerWaiter = GetListeners(provider).First(l => l.Metadata.FeatureName == FeatureAttribute.SolutionCrawler).Value as TestAsynchronousOperationListener; + var solutionCrawlerWaiter = GetListeners(provider).First(l => l.Metadata.FeatureName == FeatureAttribute.SolutionCrawlerLegacy).Value as TestAsynchronousOperationListener; sb.AppendLine("solutionCrawler"); sb.AppendLine(solutionCrawlerWaiter.Trace()); diff --git a/src/EditorFeatures/Test/Tagging/AsynchronousTaggerTests.cs b/src/EditorFeatures/Test/Tagging/AsynchronousTaggerTests.cs index 0ce99c54c6093..f6e164b3366e7 100644 --- a/src/EditorFeatures/Test/Tagging/AsynchronousTaggerTests.cs +++ b/src/EditorFeatures/Test/Tagging/AsynchronousTaggerTests.cs @@ -183,6 +183,9 @@ protected override Task ProduceTagsAsync( return Task.CompletedTask; } + + protected override bool TagEquals(TestTag tag1, TestTag tag2) + => tag1 == tag2; } private sealed class TestTaggerEventSource : AbstractTaggerEventSource diff --git a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs index 049fa9f270ac1..8e317dfccd5b4 100644 --- a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs @@ -40,7 +40,7 @@ private static void Test(Action @@ -59,7 +59,7 @@ public void TestCacheKeepsObjectAlive1() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestCacheKeepsObjectAlive2() { Test((cacheService, projectId, owner, instance) => @@ -78,7 +78,7 @@ public void TestCacheKeepsObjectAlive2() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected1() { Test((cacheService, projectId, owner, instance) => @@ -94,7 +94,7 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected1() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() { Test((cacheService, projectId, owner, instance) => @@ -110,7 +110,7 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestImplicitCacheKeepsObjectAlive1() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); @@ -144,7 +144,7 @@ private static ObjectReference PutObjectInImplicitCache(ProjectCacheServ } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestP2PReference() { var workspace = new AdhocWorkspace(); @@ -172,7 +172,7 @@ public void TestP2PReference() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestEjectFromImplicitCache() { var compilations = new List(); @@ -202,7 +202,7 @@ public void TestEjectFromImplicitCache() } [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] - [ConditionalFact(typeof(x86))] + [ConditionalFact(typeof(Bitness32))] public void TestCacheCompilationTwice() { var comp1 = CSharpCompilation.Create("1"); diff --git a/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/TextFactoryTests.cs index 152ecfa34bf32..4533f3c24a71e 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,8 +120,9 @@ 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, SourceHashAlgorithms.Default, CancellationToken.None); Assert.Equal(expectedEncoding, text.Encoding); + Assert.Equal(SourceHashAlgorithms.Default, text.ChecksumAlgorithm); } } } diff --git a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb index 388b7d61e1333..beda37d80fb1d 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb @@ -9,19 +9,27 @@ Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.UnitTests Imports Microsoft.CodeAnalysis.UnitTests.Diagnostics Imports Roslyn.Utilities +Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics <[UseExportProvider]> Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTest + Private ReadOnly _outputHelper As ITestOutputHelper + + Protected Sub New(Optional outputHelper As ITestOutputHelper = Nothing) + _outputHelper = outputHelper + End Sub + Protected Const DestinationDocument = "DestinationDocument" Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService), GetType(WorkspaceTestLogger)) Private Shared ReadOnly s_composition As TestComposition = s_compositionWithMockDiagnosticUpdateSourceRegistrationService _ .AddParts(GetType(TestAddMetadataReferenceCodeActionOperationFactoryWorkspaceService)) @@ -52,10 +60,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics Optional verifyTokens As Boolean = True, Optional fileNameToExpected As Dictionary(Of String, String) = Nothing, Optional verifySolutions As Func(Of Solution, Solution, Task) = Nothing, - Optional onAfterWorkspaceCreated As Action(Of TestWorkspace) = Nothing, + Optional onAfterWorkspaceCreated As Func(Of TestWorkspace, Task) = Nothing, Optional glyphTags As ImmutableArray(Of String) = Nothing) As Task Using workspace = TestWorkspace.CreateWorkspace(definition, composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) - onAfterWorkspaceCreated?.Invoke(workspace) + If _outputHelper IsNot Nothing Then + workspace.Services.SolutionServices.SetWorkspaceTestOutput(_outputHelper) + End If + + If onAfterWorkspaceCreated IsNot Nothing Then + Await onAfterWorkspaceCreated(workspace) + End If Dim diagnosticAndFix = Await GetDiagnosticAndFixAsync(workspace) Dim codeActions As IList(Of CodeAction) = diagnosticAndFix.Item2.Fixes.Select(Function(f) f.Action).ToList() diff --git a/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb index dd3c48f2c3c28..348eb6832d533 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb @@ -266,7 +266,7 @@ namespace CSAssembly2 Await TestAsync( input, expected, codeActionIndex:=0, addedReference:="CSAssembly1", - glyphTags:=WellKnownTagArrays.CSharpProject, onAfterWorkspaceCreated:=AddressOf WaitForSolutionCrawler) + glyphTags:=WellKnownTagArrays.CSharpProject, onAfterWorkspaceCreated:=AddressOf WaitForSymbolTreeInfoCache) End Function @@ -302,6 +302,60 @@ namespace CSAssembly2 Await TestMissing(input) End Function + + Public Async Function AddProjectReference_CSharpToCSharp_ExtensionMethod() As Task + Dim input = + + + +using System.Collections.Generic; +namespace CSAssembly1 +{ + public static class Class1 + { + public static void Goo(this int x) { } + } +} + + + + + +namespace CSAssembly2 +{ + public class Class2 + { + void Bar(int i) + { + i.$$Goo(); + } + } +} + + + + + Dim expected = + +using CSAssembly1; + +namespace CSAssembly2 +{ + public class Class2 + { + void Bar(int i) + { + i.Goo(); + } + } +} + .Value.Trim() + + Await TestAsync( + input, expected, codeActionIndex:=0, addedReference:="CSAssembly1", + glyphTags:=WellKnownTagArrays.CSharpProject, onAfterWorkspaceCreated:=AddressOf WaitForSymbolTreeInfoCache) + End Function + Public Async Function TestAddProjectReference_CSharpToCSharp_WithProjectRenamed() As Task Dim input = @@ -347,12 +401,11 @@ namespace CSAssembly2 Await TestAsync(input, expected, codeActionIndex:=0, addedReference:="NewName", glyphTags:=WellKnownTagArrays.CSharpProject, onAfterWorkspaceCreated:= - Sub(workspace As TestWorkspace) - WaitForSolutionCrawler(workspace) + Async Function(workspace As TestWorkspace) Dim project = workspace.CurrentSolution.Projects.Single(Function(p) p.AssemblyName = "CSAssembly1") workspace.OnProjectNameChanged(project.Id, "NewName", "NewFilePath") - WaitForSolutionCrawler(workspace) - End Sub) + Await WaitForSymbolTreeInfoCache(workspace) + End Function) End Function @@ -392,17 +445,16 @@ End Namespace Await TestAsync( input, expected, codeActionIndex:=0, addedReference:="VBAssembly1", - glyphTags:=WellKnownTagArrays.VisualBasicProject, onAfterWorkspaceCreated:=AddressOf WaitForSolutionCrawler) + glyphTags:=WellKnownTagArrays.VisualBasicProject, onAfterWorkspaceCreated:=AddressOf WaitForSymbolTreeInfoCache) End Function - Private Sub WaitForSolutionCrawler(workspace As TestWorkspace) - Dim solutionCrawler = DirectCast(workspace.Services.GetService(Of ISolutionCrawlerRegistrationService), SolutionCrawlerRegistrationService) - solutionCrawler.Register(workspace) - Dim provider = DirectCast(workspace.ExportProvider.GetExports(Of IIncrementalAnalyzerProvider).First( - Function(f) TypeOf f.Value Is SymbolTreeInfoIncrementalAnalyzerProvider).Value, SymbolTreeInfoIncrementalAnalyzerProvider) - Dim analyzer = provider.CreateIncrementalAnalyzer(workspace) - solutionCrawler.GetTestAccessor().WaitUntilCompletion(workspace, ImmutableArray.Create(analyzer)) - End Sub + Private Async Function WaitForSymbolTreeInfoCache(workspace As TestWorkspace) As Task + Dim service = DirectCast( + workspace.Services.GetRequiredService(Of ISymbolTreeInfoCacheService), + SymbolTreeInfoCacheServiceFactory.SymbolTreeInfoCacheService) + + Await service.GetTestAccessor().AnalyzeSolutionAsync() + End Function Public Async Function TestAddProjectReference_CSharpToVB_ExtensionMethod() As Task @@ -471,7 +523,7 @@ namespace A Await TestAsync(input, addedReference:="CSAssembly2", glyphTags:=WellKnownTagArrays.CSharpProject, - onAfterWorkspaceCreated:=AddressOf WaitForSolutionCrawler) + onAfterWorkspaceCreated:=AddressOf WaitForSymbolTreeInfoCache) End Function @@ -511,7 +563,7 @@ namespace CSAssembly2 Optional expected As String = Nothing, Optional codeActionIndex As Integer = 0, Optional addedReference As String = Nothing, - Optional onAfterWorkspaceCreated As Action(Of TestWorkspace) = Nothing, + Optional onAfterWorkspaceCreated As Func(Of TestWorkspace, Task) = Nothing, Optional glyphTags As ImmutableArray(Of String) = Nothing) As Task Dim verifySolutions As Func(Of Solution, Solution, Task) = Nothing Dim workspace As TestWorkspace = Nothing @@ -540,10 +592,12 @@ namespace CSAssembly2 Await TestAsync(definition, expected, codeActionIndex, verifySolutions:=verifySolutions, glyphTags:=glyphTags, - onAfterWorkspaceCreated:=Sub(ws As TestWorkspace) + onAfterWorkspaceCreated:=Async Function(ws As TestWorkspace) workspace = ws - onAfterWorkspaceCreated?.Invoke(ws) - End Sub) + If onAfterWorkspaceCreated IsNot Nothing Then + Await onAfterWorkspaceCreated(ws) + End If + End Function) End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb index 23b92365543b3..74b309cbb35f2 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb @@ -12,10 +12,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateMethod Partial Public Class GenerateMethodCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Private ReadOnly _outputHelper As ITestOutputHelper - Public Sub New(outputHelper As ITestOutputHelper) - _outputHelper = outputHelper + MyBase.New(outputHelper) End Sub Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) @@ -61,7 +59,7 @@ public class VBClass end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -98,7 +96,7 @@ public class VBClass end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -140,7 +138,7 @@ public class VBClass end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -174,7 +172,7 @@ end class end interface .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -216,7 +214,7 @@ end class end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -291,7 +289,7 @@ end class } .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -332,7 +330,7 @@ end class end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -373,7 +371,7 @@ end class end class .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -418,7 +416,7 @@ end class } .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -463,7 +461,7 @@ end class }]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -517,7 +515,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -587,7 +585,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -657,7 +655,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -737,7 +735,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -826,7 +824,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -911,7 +909,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -1003,7 +1001,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function @@ -1076,7 +1074,7 @@ Module Program End Module]]> .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function #Region "Normal tests" diff --git a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb index 18b2c14e02054..692c6a36a38c4 100644 --- a/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb @@ -14,10 +14,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable Partial Public Class GenerateVariableCrossLanguageTests Inherits AbstractCrossLanguageUserDiagnosticTest - Private ReadOnly _outputHelper As ITestOutputHelper - Public Sub New(outputHelper As ITestOutputHelper) - _outputHelper = outputHelper + MyBase.New(outputHelper) End Sub Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) @@ -64,7 +62,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable } .Value.Trim() - Await TestAsync(input, expected, onAfterWorkspaceCreated:=Sub(w) w.SetTestLogger(AddressOf _outputHelper.WriteLine)) + Await TestAsync(input, expected) End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 5a6fa64e6c50e..d506af6cb7578 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.FindSymbols Imports Microsoft.CodeAnalysis.FindUsages +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Remote.Testing @@ -273,8 +274,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences uiVisibleOnly As Boolean, options As FindReferencesSearchOptions) As Task - Using workspace = TestWorkspace.Create(definition, composition:=s_composition.WithTestHostParts(host)) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = TestWorkspace.Create(definition, composition:=s_composition.WithTestHostParts(host).AddParts(GetType(WorkspaceTestLogger))) + workspace.Services.SolutionServices.SetWorkspaceTestOutput(_outputHelper) For Each cursorDocument In workspace.Documents.Where(Function(d) d.CursorPosition.HasValue) Dim cursorPosition = cursorDocument.CursorPosition.Value diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index b052543170a2e..f2af9d985fb33 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -2958,6 +2958,115 @@ class Program End Using End Function + + Public Async Function SelectEnumMemberAdditionalFilterTextMatchOverInferiorFilterTextMatch() As Task + Using state = TestStateFactory.CreateCSharpTestState( + +public enum Colors +{ + Red, + Green +} + +class Program +{ + Colors GreenNode { get; } + void M() + { + Colors c = Green$$ + } +} ) + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("Colors.Green", "GreenNode") + ' select full match "Colors.Green" over prefix match "GreenNode" + Await state.AssertSelectedCompletionItem("Colors.Green", isHardSelected:=True) + End Using + End Function + + + Public Async Function DoNotSelectEnumMemberAdditionalFilterTextMatchOverEqualFilterTextMatch() As Task + Using state = TestStateFactory.CreateCSharpTestState( + +public enum Colors +{ + Red, + Green +} + +class Program +{ + Colors Green { get; } + void M() + { + Colors c = gree$$ + } +} ) + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("Colors.Green", "Green") + ' Select FilterText match "Green" over AdditionalFilterText match "Colors.Green" + Await state.AssertSelectedCompletionItem("Green", isHardSelected:=True) + End Using + End Function + + + Public Async Function SelectStaticMemberAdditionalFilterTextMatchOverInferiorFilterTextMatch() As Task + Using state = TestStateFactory.CreateCSharpTestState( + +public class MyArray +{ + public static MyArray Empty { get; } +} + +class Program +{ + string EmptyString = ""; + void M() + { + MyArray c = Empty$$ + } +} ) + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("MyArray.Empty", "EmptyString") + ' select full match "MyArray.Empty" over prefix match "EmptyString" + Await state.AssertSelectedCompletionItem("MyArray.Empty", isHardSelected:=True) + End Using + End Function + + + Public Async Function SelectCompletionListStaticMemberAdditionalFilterTextMatchOverInferiorFilterTextMatch() As Task + Using state = TestStateFactory.CreateCSharpTestState( + + public class SomeType + { } + + public static class TypeContainer + { + public static SomeType Foo1 = new SomeType(); + public static Program Foo2 = new Program(); + } + + public class Program + { + void Goo() + { + var myFoo = true; + SomeType c = $$ + } + } +} ]]>) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("myFoo", "TypeContainer", "TypeContainer.Foo1", "TypeContainer.Foo2") + + state.SendTypeChars("foo") + Await state.AssertSelectedCompletionItem("TypeContainer.Foo1", isHardSelected:=True) + End Using + End Function + Public Async Function CompletionEnumTypeAndValuesWithAlias() As Task @@ -4135,7 +4244,7 @@ class C state.SendBackspace() Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim slowProvider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of SlowProvider)().Single() + Dim slowProvider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of SlowProvider)().Single() slowProvider.checkpoint.Release() Await state.AssertNoCompletionSession() End Using @@ -5066,7 +5175,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() state.Workspace.GlobalOptions.SetGlobalOption( New OptionKey(CompletionViewOptions.BlockForCompletionItems, LanguageNames.CSharp), False) @@ -5121,7 +5230,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() state.Workspace.GlobalOptions.SetGlobalOption( New OptionKey(CompletionViewOptions.BlockForCompletionItems, LanguageNames.CSharp), False) @@ -5199,7 +5308,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() Dim globalOptions = state.Workspace.GetService(Of IGlobalOptionService) globalOptions.SetGlobalOption(New OptionKey(CompletionViewOptions.BlockForCompletionItems, LanguageNames.CSharp), False) @@ -5268,7 +5377,7 @@ class C Dim globalOptions = state.Workspace.GetService(Of IGlobalOptionService) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of BooleanTaskControlledCompletionProvider)().Single() #Disable Warning BC42358 ' Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(Function() @@ -5712,6 +5821,58 @@ class C End Using End Function + + + Public Async Function TestEnumMembersMatchTargetType() As Task + Using state = TestStateFactory.CreateCSharpTestState( + ) + + state.SendInvokeCompletionList() + Await state.WaitForUIRenderedAsync() + + Await state.AssertCompletionItemsContainAll("Color.Red", "Color.Green", "Color.Blue", "Color") + + Dim oldFilters = state.GetCompletionItemFilters() + Dim newFiltersBuilder = ArrayBuilder(Of Data.CompletionFilterWithState).GetInstance() + + Dim hasTargetTypedFilter = False + For Each oldState In oldFilters + + If Object.ReferenceEquals(oldState.Filter, FilterSet.TargetTypedFilter) Then + hasTargetTypedFilter = True + newFiltersBuilder.Add(oldState.WithSelected(True)) + Continue For + End If + + newFiltersBuilder.Add(oldState.WithSelected(False)) + Next + + Assert.True(hasTargetTypedFilter) + + state.RaiseFiltersChanged(newFiltersBuilder.ToImmutableAndFree()) + + Await state.WaitForUIRenderedAsync() + Await state.AssertCompletionItemsContainAll("Color.Red", "Color.Green", "Color.Blue") + Await state.AssertCompletionItemsDoNotContainAny("Color") + End Using + End Function + @@ -5759,7 +5920,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of MultipleChangeCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of MultipleChangeCompletionProvider)().Single() Dim testDocument = state.Workspace.Documents(0) Dim textBuffer = testDocument.GetTextBuffer() @@ -5828,7 +5989,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of MultipleChangeCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of MultipleChangeCompletionProvider)().Single() Dim testDocument = state.Workspace.Documents(0) Dim textBuffer = testDocument.GetTextBuffer() @@ -6575,7 +6736,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() Await state.SendCommitUniqueCompletionListItemAsync() Await state.AssertNoCompletionSession() @@ -6601,7 +6762,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() state.SendTypeChars(".len") Await state.AssertCompletionItemsContainAll("Length", "★ Length") @@ -6632,7 +6793,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() state.Workspace.GlobalOptions.SetGlobalOption( New OptionKey(CompletionOptionsStorage.TriggerOnDeletion, LanguageNames.CSharp), True) @@ -6663,7 +6824,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() state.SendInvokeCompletionList() state.SendSelectCompletionItem("★ Length") @@ -6689,7 +6850,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockWeirdProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockWeirdProvider)().Single() Await state.SendCommitUniqueCompletionListItemAsync() Await state.AssertNoCompletionSession() @@ -6715,7 +6876,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockWeirdProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockWeirdProvider)().Single() state.SendTypeChars(".len") Await state.AssertCompletionItemsContainAll("Length", "★ Length", "★ Length2") @@ -6743,7 +6904,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() state.Workspace.GlobalOptions.SetGlobalOption( New OptionKey(CompletionOptionsStorage.TriggerOnDeletion, LanguageNames.CSharp), True) @@ -6795,7 +6956,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of IntelliCodeMockProvider)().Single() state.Workspace.GlobalOptions.SetGlobalOption( New OptionKey(CompletionOptionsStorage.TriggerOnDeletion, LanguageNames.CSharp), True) @@ -9468,7 +9629,7 @@ public class AA extraExportedTypes:={GetType(TestMatchPriorityCompletionProvider)}.ToList()) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestMatchPriorityCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestMatchPriorityCompletionProvider)().Single() provider.AddItems(New(displayText As String, matchPriority As Integer)() { ("item1", MatchPriority.Default - 1), @@ -9506,7 +9667,7 @@ public class AA extraExportedTypes:={GetType(TestMatchPriorityCompletionProvider)}.ToList()) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestMatchPriorityCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestMatchPriorityCompletionProvider)().Single() provider.AddItems(New(displayText As String, matchPriority As Integer)() { ("item1", MatchPriority.Preselect), @@ -9997,7 +10158,7 @@ class C showCompletionInArgumentLists:=showCompletionInArgumentLists) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim notifyProvider As NotifyCommittingItemCompletionProvider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of NotifyCommittingItemCompletionProvider)().Single() + Dim notifyProvider As NotifyCommittingItemCompletionProvider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of NotifyCommittingItemCompletionProvider)().Single() notifyProvider.Reset() state.SendInvokeCompletionList() @@ -10080,7 +10241,7 @@ class C state.TextView.Options.SetOptionValue(DefaultOptions.ResponsiveCompletionOptionId, True) Dim completionService = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() ' completion list shouldn't have expand item until we release the checkpoint state.SendTypeChars("TestUnimp") @@ -10139,7 +10300,7 @@ class C state.TextView.Options.SetOptionValue(DefaultOptions.ResponsiveCompletionOptionId, True) Dim completionService = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() ' completion list shouldn't have expand item until we release the checkpoint state.SendTypeChars("TestUnimp") @@ -10198,7 +10359,7 @@ class C state.TextView.Options.SetOptionValue(DefaultOptions.ResponsiveCompletionOptionId, True) Dim completionService = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TestProvider)().Single() ' First we enable delay for expand item, and trigger completion with test provider blocked ' this would ensure completion list don't have expand item until we release the checkpoint @@ -10523,5 +10684,58 @@ class MyClass Await state.AssertSelectedCompletionItem(displayText:="cw", inlineDescription:="Console.WriteLine", isHardSelected:=True) End Using End Function + + + + Public Async Function HardSelectBreakAfterYieldIfNoYieldType(hasYieldType As Boolean) As Task + Dim yieldDeclaration = If(hasYieldType, "public class yield{}", String.Empty) + + Using state = TestStateFactory.CreateCSharpTestState( + +namespace NS +{ + <%= yieldDeclaration %> + + class C + { + public static void M() + { + yield bre$$ + } + } +} + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem(displayText:="break", isHardSelected:=Not hasYieldType) + + End Using + End Function + + + + Public Async Function HardSelectReturnAfterYieldIfNoYieldType(hasYieldType As Boolean) As Task + Dim yieldDeclaration = If(hasYieldType, "public class yield{}", String.Empty) + Using state = TestStateFactory.CreateCSharpTestState( + +namespace NS +{ + <%= yieldDeclaration %> + + class C + { + public static void M() + { + yield ret$$ + } + } +} + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem(displayText:="return", isHardSelected:=Not hasYieldType) + + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb index 7b7c23362274b..a1e7d40cb6028 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense @@ -33,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Using workspace = TestWorkspace.Create(workspaceDefinition, composition:=composition) Dim document = workspace.CurrentSolution.Projects.First.Documents.First - Dim completionService = New TestCompletionService(workspace.Services.SolutionServices) + Dim completionService = New TestCompletionService(workspace.Services.SolutionServices, workspace.GetService(Of IAsynchronousOperationListenerProvider)()) Dim list = Await completionService.GetCompletionsAsync( document, caretPosition:=0, CompletionOptions.Default, OptionValueSet.Empty, CompletionTrigger.Invoke) @@ -47,8 +48,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Friend Class TestCompletionService Inherits CompletionService - Public Sub New(services As SolutionServices) - MyBase.New(services) + Public Sub New(services As SolutionServices, listenerProvider As IAsynchronousOperationListenerProvider) + MyBase.New(services, listenerProvider) End Sub Public Overrides ReadOnly Property Language As String diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb index fad5cd85951c8..b000ee52f8e39 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense @@ -54,7 +55,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Inherits CompletionService Public Sub New(services As SolutionServices) - MyBase.New(services) + MyBase.New(services, AsynchronousOperationListenerProvider.NullProvider) End Sub Public Overrides ReadOnly Property Language As String diff --git a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb index 16bc1beab49df..0b93f556aad78 100644 --- a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb @@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub InvokeSignatureHelpWithoutDocumentShouldNotStartNewSession() Dim emptyProvider = New Mock(Of IDocumentProvider)(MockBehavior.Strict) emptyProvider.Setup(Function(p) p.GetDocument(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))).Returns(DirectCast(Nothing, Document)) - Dim controller As Controller = CreateController(documentProvider:=emptyProvider) + Dim controller As Controller = CreateController(CreateWorkspace(), documentProvider:=emptyProvider) GetMocks(controller).PresenterSession.Setup(Sub(p) p.Dismiss()) @@ -40,7 +40,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub InvokeSignatureHelpWithDocumentShouldStartNewSession() - Dim controller = CreateController() + Dim controller = CreateController(CreateWorkspace()) GetMocks(controller).Presenter.Verify(Function(p) p.CreateSession(It.IsAny(Of ITextView), It.IsAny(Of ITextBuffer), It.IsAny(Of ISignatureHelpSession)), Times.Once) End Sub @@ -50,14 +50,14 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim presenterSession = New Mock(Of ISignatureHelpPresenterSession)(MockBehavior.Strict) presenterSession.Setup(Sub(p) p.Dismiss()) - Dim controller = CreateController(presenterSession:=presenterSession, items:={}, waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), presenterSession:=presenterSession, items:={}, waitForPresentation:=True) GetMocks(controller).PresenterSession.Verify(Sub(p) p.Dismiss(), Times.Once) End Sub Public Sub UpKeyShouldDismissWhenThereIsOnlyOneItem() - Dim controller = CreateController(items:=CreateItems(1), waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(1), waitForPresentation:=True) GetMocks(controller).PresenterSession.Setup(Sub(p) p.Dismiss()) @@ -69,7 +69,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub UpKeyShouldNavigateWhenThereAreMultipleItems() - Dim controller = CreateController(items:=CreateItems(2), waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(2), waitForPresentation:=True) GetMocks(controller).PresenterSession.Setup(Sub(p) p.SelectPreviousItem()) @@ -89,7 +89,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense slowProvider.Setup(Function(p) p.IsRetriggerCharacter(" "c)).Returns(True) slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), options, It.IsAny(Of CancellationToken))) _ .Returns(Task.FromResult(New SignatureHelpItems(CreateItems(2), TextSpan.FromBounds(0, 0), selectedItem:=0, argumentIndex:=0, argumentCount:=0, argumentName:=Nothing))) - Dim controller = CreateController(provider:=slowProvider.Object, waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), provider:=slowProvider.Object, waitForPresentation:=True) ' Now force an update to the model that will result in stopping the session slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), options, It.IsAny(Of CancellationToken))) _ @@ -111,7 +111,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub DownKeyShouldNotBlockOnModelComputation() Dim options = New SignatureHelpOptions() Dim mre = New ManualResetEvent(False) - Dim controller = CreateController(items:=CreateItems(2), waitForPresentation:=False) + Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(2), waitForPresentation:=False) Dim slowProvider = New Mock(Of ISignatureHelpProvider)(MockBehavior.Strict) slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), Options, It.IsAny(Of CancellationToken))) _ .Returns(Function() @@ -131,7 +131,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub UpKeyShouldNotBlockOnModelComputation() Dim options = New SignatureHelpOptions() Dim mre = New ManualResetEvent(False) - Dim controller = CreateController(items:=CreateItems(2), waitForPresentation:=False) + Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(2), waitForPresentation:=False) Dim slowProvider = New Mock(Of ISignatureHelpProvider)(MockBehavior.Strict) slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), options, It.IsAny(Of CancellationToken))) _ .Returns(Function() @@ -149,9 +149,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Async Function UpKeyShouldBlockOnRecomputationAfterPresentation() As Task - Dim exportProvider = EditorTestCompositions.EditorFeatures.ExportProviderFactory.CreateExportProvider() - Dim threadingContext = exportProvider.GetExportedValue(Of IThreadingContext)() Dim options = New SignatureHelpOptions() + Dim workspace = CreateWorkspace() + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim worker = Async Function() Dim slowProvider = New Mock(Of ISignatureHelpProvider)(MockBehavior.Strict) @@ -161,7 +161,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense .Returns(Task.FromResult(New SignatureHelpItems(CreateItems(2), TextSpan.FromBounds(0, 0), selectedItem:=0, argumentIndex:=0, argumentCount:=0, argumentName:=Nothing))) Await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync() - Dim controller = CreateController(provider:=slowProvider.Object, waitForPresentation:=True) + Dim controller = CreateController(workspace, provider:=slowProvider.Object, waitForPresentation:=True) ' Update session so that providers are requeried. ' SlowProvider now blocks on the checkpoint's task. @@ -199,7 +199,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub DownKeyShouldNavigateWhenThereAreMultipleItems() - Dim controller = CreateController(items:=CreateItems(2), waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(2), waitForPresentation:=True) GetMocks(controller).PresenterSession.Setup(Sub(p) p.SelectNextItem()) @@ -213,7 +213,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub UpAndDownKeysShouldStillNavigateWhenDuplicateItemsAreFiltered() Dim item = CreateItems(1).Single() - Dim controller = CreateController(items:={item, item}, waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), items:={item, item}, waitForPresentation:=True) GetMocks(controller).PresenterSession.Setup(Sub(p) p.Dismiss()) @@ -225,7 +225,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub CaretMoveWithActiveSessionShouldRecomputeModel() - Dim controller = CreateController(waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), waitForPresentation:=True) Mock.Get(GetMocks(controller).View.Object.Caret).Raise(Sub(c) AddHandler c.PositionChanged, Nothing, New CaretPositionChangedEventArgs(Nothing, Nothing, Nothing)) controller.WaitForController() @@ -236,7 +236,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub RetriggerActiveSessionOnClosingBrace() - Dim controller = CreateController(waitForPresentation:=True) + Dim controller = CreateController(CreateWorkspace(), waitForPresentation:=True) DirectCast(controller, IChainedCommandHandler(Of TypeCharCommandArgs)).ExecuteCommand( New TypeCharCommandArgs(CreateMock(Of ITextView), CreateMock(Of ITextBuffer), ")"c), @@ -249,7 +249,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Sub TypingNonTriggerCharacterShouldNotRequestDocument() - Dim controller = CreateController(triggerSession:=False) + Dim controller = CreateController(CreateWorkspace(), triggerSession:=False) DirectCast(controller, IChainedCommandHandler(Of TypeCharCommandArgs)).ExecuteCommand( New TypeCharCommandArgs(CreateMock(Of ITextView), CreateMock(Of ITextBuffer), "a"c), @@ -265,19 +265,23 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Return result End Function - Private Shared Function CreateController(Optional documentProvider As Mock(Of IDocumentProvider) = Nothing, - Optional presenterSession As Mock(Of ISignatureHelpPresenterSession) = Nothing, - Optional items As IList(Of SignatureHelpItem) = Nothing, - Optional provider As ISignatureHelpProvider = Nothing, - Optional waitForPresentation As Boolean = False, - Optional triggerSession As Boolean = True) As Controller - Dim workspace = TestWorkspace.CreateWorkspace( + Private Shared Function CreateWorkspace() As TestWorkspace + Return TestWorkspace.CreateWorkspace( - ) + , composition:=EditorTestCompositions.EditorFeatures) + End Function + + Private Shared Function CreateController(workspace As TestWorkspace, + Optional documentProvider As Mock(Of IDocumentProvider) = Nothing, + Optional presenterSession As Mock(Of ISignatureHelpPresenterSession) = Nothing, + Optional items As IList(Of SignatureHelpItem) = Nothing, + Optional provider As ISignatureHelpProvider = Nothing, + Optional waitForPresentation As Boolean = False, + Optional triggerSession As Boolean = True) As Controller Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id) Dim threadingContext = workspace.GetService(Of IThreadingContext) diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 17a754a2705dd..4c6bf895b2ed5 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -6,12 +6,15 @@ Imports System.Collections.Immutable Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis.Completion +Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Snippets Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Projection @@ -407,7 +410,7 @@ End Class extraExportedTypes:={GetType(TriggeredCompletionProvider)}.ToList()) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() Await state.AssertNoCompletionSession() state.SendTypeChars(".M") @@ -445,7 +448,7 @@ End Class extraExportedTypes:={GetType(TriggeredCompletionProvider)}.ToList()) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() Await state.AssertNoCompletionSession() state.SendTypeChars(".Ma") @@ -479,7 +482,7 @@ End Class extraExportedTypes:={GetType(TriggeredCompletionProvider)}.ToList()) Dim completionService = state.Workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetRequiredService(Of CompletionService)() - Dim provider = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() + Dim provider = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).OfType(Of TriggeredCompletionProvider)().Single() Await state.AssertNoCompletionSession() state.SendTypeChars(".Ma") @@ -3493,5 +3496,116 @@ Class C Return shortcut = "Shortcut" End Function End Class + + + Public Async Function SelectEnumMemberAdditionalFilterTextMatchOverFilterTextMatchWithLowerPriority() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + +Enum Colors + Red + Green +End Enum + +Class Program + Property Green As Program + Sub M() + Dim x As Colors = Green$$ + End Sub +End Class +) + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("Colors.Green", "Green") + Await state.AssertSelectedCompletionItem("Colors.Green", isHardSelected:=True) + End Using + End Function + + + Public Async Function DoNotSelectEnumMemberAdditionalFilterTextMatchOverBetterFilterTextMatch() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + +Enum Colors + Red + Green +End Enum + +Class Program + Sub M() + Dim gree = "" + Dim x As Colors = gree$$ + End Sub +End Class + ) + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("Colors.Green", "gree") + Await state.AssertSelectedCompletionItem("gree", isHardSelected:=True) + End Using + End Function + + + Public Async Function TestEnumMemberContainsLocal() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + +Enum E + A +End Enum + +Class C + Sub M() + Const e As E = e$$ + End Sub +End Class + ) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("e", "E", "E.A") + Await state.AssertCompletionItemsDoNotContainAny("e As E") + End Using + End Function + + + Public Async Function TestEnumMembersMatchTargetType() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + +Enum Colors + Red + Green +End Enum + +Class Program + Property Green As Program + Sub M() + Dim x As Colors = $$ + End Sub +End Class +) + state.SendInvokeCompletionList() + Await state.WaitForUIRenderedAsync() + + Await state.AssertCompletionItemsContainAll("Colors.Red", "Colors.Green", "Colors") + + Dim oldFilters = state.GetCompletionItemFilters() + Dim newFiltersBuilder = ArrayBuilder(Of Data.CompletionFilterWithState).GetInstance() + + Dim hasTargetTypedFilter = False + For Each oldState In oldFilters + + If Object.ReferenceEquals(oldState.Filter, FilterSet.TargetTypedFilter) Then + hasTargetTypedFilter = True + newFiltersBuilder.Add(oldState.WithSelected(True)) + Continue For + End If + + newFiltersBuilder.Add(oldState.WithSelected(False)) + Next + + Assert.True(hasTargetTypedFilter) + + state.RaiseFiltersChanged(newFiltersBuilder.ToImmutableAndFree()) + + Await state.WaitForUIRenderedAsync() + Await state.AssertCompletionItemsContainAll("Colors.Red", "Colors.Green") + Await state.AssertCompletionItemsDoNotContainAny("Colors") + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/PasteTracking/PasteTrackingTestState.vb b/src/EditorFeatures/Test2/PasteTracking/PasteTrackingTestState.vb index 90a52fc67fde0..7eca83faaa202 100644 --- a/src/EditorFeatures/Test2/PasteTracking/PasteTrackingTestState.vb +++ b/src/EditorFeatures/Test2/PasteTracking/PasteTrackingTestState.vb @@ -21,21 +21,13 @@ Namespace Microsoft.CodeAnalysis.PasteTracking Public ReadOnly Property Workspace As TestWorkspace - Public Sub New(workspaceElement As XElement, Optional exportProvider As ExportProvider = Nothing) - Workspace = TestWorkspace.CreateWorkspace(workspaceElement, exportProvider:=exportProvider) - PasteTrackingService = GetExportedValue(Of PasteTrackingService)() - PasteTrackingPasteCommandHandler = GetExportedValue(Of PasteTrackingPasteCommandHandler)() - FormatCommandHandler = GetExportedValue(Of FormatCommandHandler)() + Public Sub New(workspaceElement As XElement, Optional composition As TestComposition = Nothing) + Workspace = TestWorkspace.CreateWorkspace(workspaceElement, composition:=composition) + PasteTrackingService = Workspace.GetService(Of PasteTrackingService)() + PasteTrackingPasteCommandHandler = Workspace.GetService(Of PasteTrackingPasteCommandHandler)() + FormatCommandHandler = Workspace.GetService(Of FormatCommandHandler)() End Sub - Public Function GetService(Of T)() As T - Return Workspace.GetService(Of T)() - End Function - - Public Function GetExportedValue(Of T)() As T - Return Workspace.ExportProvider.GetExportedValue(Of T)() - End Function - Public Function OpenDocument(projectName As String, fileName As String) As TestHostDocument Dim hostDocument = Workspace.Documents.FirstOrDefault(Function(document) document.Project.Name = projectName AndAlso document.Name = fileName) @@ -60,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.PasteTracking Public Sub InsertText(hostDocument As TestHostDocument, insertedText As String) Dim textView = hostDocument.GetTextView() - Dim editorOperations = GetService(Of IEditorOperationsFactoryService)().GetEditorOperations(textView) + Dim editorOperations = Workspace.GetService(Of IEditorOperationsFactoryService)().GetEditorOperations(textView) editorOperations.InsertText(insertedText) End Sub @@ -70,7 +62,7 @@ Namespace Microsoft.CodeAnalysis.PasteTracking Dim caretPosition = textView.Caret.Position.BufferPosition.Position Dim trackingSpan = textView.TextSnapshot.CreateTrackingSpan(caretPosition, 0, SpanTrackingMode.EdgeInclusive) - Dim editorOperations = GetService(Of IEditorOperationsFactoryService)().GetEditorOperations(textView) + Dim editorOperations = Workspace.GetService(Of IEditorOperationsFactoryService)().GetEditorOperations(textView) Dim insertAction As Action = Sub() editorOperations.InsertText(pastedText) Dim pasteCommandArgs = New PasteCommandArgs(textView, textView.TextBuffer) diff --git a/src/EditorFeatures/Test2/Rename/RenameEngineResult.vb b/src/EditorFeatures/Test2/Rename/RenameEngineResult.vb index b672d5e1ec86b..07f6f3229da64 100644 --- a/src/EditorFeatures/Test2/Rename/RenameEngineResult.vb +++ b/src/EditorFeatures/Test2/Rename/RenameEngineResult.vb @@ -7,6 +7,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeCleanup Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Remote.Testing Imports Microsoft.CodeAnalysis.Rename @@ -56,14 +57,17 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename Optional expectFailure As Boolean = False, Optional sourceGenerator As ISourceGenerator = Nothing) As RenameEngineResult - Dim composition = EditorTestCompositions.EditorFeatures.AddParts(GetType(NoCompilationContentTypeLanguageService), GetType(NoCompilationContentTypeDefinitions)) + Dim composition = EditorTestCompositions.EditorFeatures.AddParts( + GetType(NoCompilationContentTypeLanguageService), + GetType(NoCompilationContentTypeDefinitions), + GetType(WorkspaceTestLogger)) If host = RenameTestHost.OutOfProcess_SingleCall OrElse host = RenameTestHost.OutOfProcess_SplitCall Then - composition = composition.WithTestHostParts(Remote.Testing.TestHost.OutOfProcess) + composition = composition.WithTestHostParts(TestHost.OutOfProcess) End If Dim workspace = TestWorkspace.CreateWorkspace(workspaceXml, composition:=composition) - workspace.SetTestLogger(AddressOf helper.WriteLine) + workspace.Services.SolutionServices.SetWorkspaceTestOutput(helper) If sourceGenerator IsNot Nothing Then workspace.OnAnalyzerReferenceAdded(workspace.CurrentSolution.ProjectIds.Single(), New TestGeneratorReference(sourceGenerator)) diff --git a/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests.vb b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests.vb new file mode 100644 index 0000000000000..ff2a6f205a61e --- /dev/null +++ b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests.vb @@ -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. + +Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.UnitTesting + + Partial Public Class UnitTestingSearchHelpersTests + Private Shared ReadOnly s_inProcessComposition As TestComposition = EditorTestCompositions.EditorFeatures + Private Shared ReadOnly s_outOffProcessComposition As TestComposition = s_inProcessComposition.WithTestHostParts(TestHost.OutOfProcess) + + Private Shared Async Function Test(query As UnitTestingSearchQuery, workspace As TestWorkspace) As Task + Dim project = workspace.CurrentSolution.Projects.Single() + + Dim actualLocations = Await UnitTestingSearchHelpers.GetSourceLocationsAsync( + project, query, cancellationToken:=Nothing) + + Dim expectedLocations = workspace.Documents.Single().SelectedSpans + + Assert.Equal(expectedLocations.Count, actualLocations.Length) + + For i = 0 To expectedLocations.Count - 1 + Dim expected = expectedLocations(i) + Dim actual = actualLocations(i) + + Assert.Equal(expected, actual.DocumentSpan.SourceSpan) + Next + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_CSharp.vb b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_CSharp.vb new file mode 100644 index 0000000000000..270853fd6b6e8 --- /dev/null +++ b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_CSharp.vb @@ -0,0 +1,457 @@ +' 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.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.UnitTesting + Partial Public Class UnitTestingSearchHelpersTests + Private Shared Async Function TestCSharp(text As String, query As UnitTestingSearchQuery, host As TestHost) As Task + Using workspace = TestWorkspace.CreateCSharp(text, + composition:=If(host = TestHost.OutOfProcess, s_outOffProcessComposition, s_inProcessComposition)) + + Await Test(query, workspace) + End Using + End Function + + + Public Async Function CS_TestType1(host As TestHost) As Task + Await TestCSharp(" +[Test] +class [|Outer|] +{ +}", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function CS_TestType1_NoAttribute(host As TestHost) As Task + Await TestCSharp(" +class [|Outer|] +{ +}", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function CS_TestType1_CaseSensitive(host As TestHost) As Task + Await TestCSharp(" +[Test] +class Outer +{ +}", UnitTestingSearchQuery.ForType("outer"), host) + End Function + + + Public Async Function CS_TestGenericType1(host As TestHost) As Task + Await TestCSharp(" +[Test] +class [|Outer|] +{ +}", UnitTestingSearchQuery.ForType("Outer`1"), host) + End Function + + + Public Async Function CS_TestGenericType2(host As TestHost) As Task + Await TestCSharp(" +[Test] +class Outer +{ +}", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function CS_TestGenericType2_NonStrict(host As TestHost) As Task + Await TestCSharp(" +[Test] +class [|Outer|] +{ +}", UnitTestingSearchQuery.ForType("Outer", strict:=False), host) + End Function + + + Public Async Function CS_TestGenericType3(host As TestHost) As Task + Await TestCSharp(" +[Test] +class Outer +{ +}", UnitTestingSearchQuery.ForType("Outer`2"), host) + End Function + + + Public Async Function CS_TestGenericType3_NonStrict(host As TestHost) As Task + Await TestCSharp(" +[Test] +class [|Outer|] +{ +}", UnitTestingSearchQuery.ForType("Outer`2", strict:=False), host) + End Function + + + Public Async Function CS_TestNestedType1(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function CS_TestNestedType1_NoAttribute1(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function CS_TestNestedType1_NoAttribute2(host As TestHost) As Task + Await TestCSharp(" +[Test] +class Outer +{ + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function CS_TestNestedType2(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer+Inner"), host) + End Function + + + Public Async Function CS_TestNestedType3(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer+Inner`1"), host) + End Function + + + Public Async Function CS_TestNestedType4(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer`1+Inner"), host) + End Function + + + Public Async Function CS_TestNestedType5(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + class [|Inner|] + { + } +}", UnitTestingSearchQuery.ForType("Outer`1+Inner`1"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace1(host As TestHost) As Task + Await TestCSharp(" +namespace N +{ + [Test] + class [|Outer|] + { + } +}", UnitTestingSearchQuery.ForType("N.Outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace1_CaseSensitive1(host As TestHost) As Task + Await TestCSharp(" +namespace N +{ + [Test] + class Outer + { + } +}", UnitTestingSearchQuery.ForType("N.outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace1_CaseSensitive2(host As TestHost) As Task + Await TestCSharp(" +namespace N +{ + [Test] + class Outer + { + } +}", UnitTestingSearchQuery.ForType("n.Outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace1_CaseSensitive3(host As TestHost) As Task + Await TestCSharp(" +namespace N +{ + [Test] + class Outer + { + } +}", UnitTestingSearchQuery.ForType("n.outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace2(host As TestHost) As Task + Await TestCSharp(" +namespace N +{ + [Test] + class Outer + { + } +}", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace3(host As TestHost) As Task + Await TestCSharp(" +namespace N1.N2 +{ + [Test] + class [|Outer|] + { + } +}", UnitTestingSearchQuery.ForType("N1.N2.Outer"), host) + End Function + + + Public Async Function CS_TestTypeWithNamespace4(host As TestHost) As Task + Await TestCSharp(" +namespace N1 +{ + namespace N2 + { + [Test] + class [|Outer|] + { + } + } +}", UnitTestingSearchQuery.ForType("N1.N2.Outer"), host) + End Function + + + Public Async Function CS_TestMethod1(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|]() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod1_NoAttribute1(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + void Goo() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod1_NoAttribute2(host As TestHost) As Task + Await TestCSharp(" +[Test] +class Outer +{ + void Goo() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod2(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void Goo() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod2_NonStrict(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|]() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=0, strict:=False), host) + End Function + + + Public Async Function CS_TestMethod3(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void Goo() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + + + Public Async Function CS_TestMethod3_NonStrict(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|]() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1, strict:=False), host) + End Function + + + Public Async Function CS_TestMethod4(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void Goo() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod5(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void Goo(int i) { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod6(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|]() { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod7(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|](int a) { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + + + Public Async Function CS_TestMethod8(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|](int a) { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=1), host) + End Function + + + Public Async Function CS_TestMethod9(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + class Inner + { + [Test] + void [|Goo|]() { } + } +}", UnitTestingSearchQuery.ForMethod("Outer+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod10(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + class Inner + { + [Test] + void [|Goo|]() { } + } +}", UnitTestingSearchQuery.ForMethod("Outer+Inner`1", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod11(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + class Inner + { + [Test] + void [|Goo|]() { } + } +}", UnitTestingSearchQuery.ForMethod("Outer`1+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod12(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + class Inner + { + [Test] + void [|Goo|]() { } + } +}", UnitTestingSearchQuery.ForMethod("Outer`1+Inner`1", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestMethod13(host As TestHost) As Task + Await TestCSharp(" +namespace N1.N2 +{ + class Outer + { + class Inner + { + [Test] + void [|Goo|]() { } + } + } +}", UnitTestingSearchQuery.ForMethod("N1.N2.Outer+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function CS_TestExtensionMethod1(host As TestHost) As Task + Await TestCSharp(" +class Outer +{ + [Test] + void [|Goo|](this Outer o) { } +}", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_VisualBasic.vb b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_VisualBasic.vb new file mode 100644 index 0000000000000..d0c08939d3183 --- /dev/null +++ b/src/EditorFeatures/Test2/UnitTesting/UnitTestingSearchHelpersTests_VisualBasic.vb @@ -0,0 +1,409 @@ +' 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.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.UnitTesting + Public Class UnitTestingSearchHelpersTests + Private Shared Async Function TestVisualBasic(text As String, query As UnitTestingSearchQuery, host As TestHost) As Task + Using workspace = TestWorkspace.CreateVisualBasic(text, + composition:=If(host = TestHost.OutOfProcess, s_outOffProcessComposition, s_inProcessComposition)) + + Await Test(query, workspace) + End Using + End Function + + + Public Async Function VB_TestType1(host As TestHost) As Task + Await TestVisualBasic(" + +class [|Outer|] +end class +", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function VB_TestType1_NoAttribute(host As TestHost) As Task + Await TestVisualBasic(" +class [|Outer|] +end class +", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function VB_TestType1_CaseInsensitive(host As TestHost) As Task + Await TestVisualBasic(" + +class [|Outer|] +end class +", UnitTestingSearchQuery.ForType("outer"), host) + End Function + + + Public Async Function VB_TestGenericType1(host As TestHost) As Task + Await TestVisualBasic(" + +class [|Outer|](of T) +end class +", UnitTestingSearchQuery.ForType("Outer`1"), host) + End Function + + + Public Async Function VB_TestGenericType2(host As TestHost) As Task + Await TestVisualBasic(" + +class Outer(of T) +end class +", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function VB_TestGenericType3(host As TestHost) As Task + Await TestVisualBasic(" + +class Outer(of T) +end class +", UnitTestingSearchQuery.ForType("Outer`2"), host) + End Function + + + Public Async Function VB_TestNestedType1(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + class [|Inner|] + end class +end class +", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function VB_TestNestedType1_NoAttribute1(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + class [|Inner|] + end class +end class +", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function VB_TestNestedType1_NoAttribute2(host As TestHost) As Task + Await TestVisualBasic(" + +class Outer + class [|Inner|] + end class +end class +", UnitTestingSearchQuery.ForType("Outer.Inner"), host) + End Function + + + Public Async Function VB_TestNestedType2(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + class [|Inner|] + end class +end class +", UnitTestingSearchQuery.ForType("Outer+Inner"), host) + End Function + + + Public Async Function VB_TestNestedType3(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + class [|Inner|](of T) + end class +end class +", UnitTestingSearchQuery.ForType("Outer+Inner`1"), host) + End Function + + + Public Async Function VB_TestNestedType4(host As TestHost) As Task + Await TestVisualBasic(" +class Outer(of T) + + class [|Inner|] + end class +end class +", UnitTestingSearchQuery.ForType("Outer`1+Inner"), host) + End Function + + + Public Async Function VB_TestNestedType5(host As TestHost) As Task + Await TestVisualBasic(" +class Outer(of T) + + class [|Inner|](of U) + end class +end class +", UnitTestingSearchQuery.ForType("Outer`1+Inner`1"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace1(host As TestHost) As Task + Await TestVisualBasic(" +namespace N + + class [|Outer|] + end class +end namespace", UnitTestingSearchQuery.ForType("N.Outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace1_CaseInsensitive1(host As TestHost) As Task + Await TestVisualBasic(" +namespace N + + class [|Outer|] + end class +end namespace", UnitTestingSearchQuery.ForType("n.Outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace1_CaseInsensitive2(host As TestHost) As Task + Await TestVisualBasic(" +namespace N + + class [|Outer|] + end class +end namespace", UnitTestingSearchQuery.ForType("N.outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace1_CaseInsensitive3(host As TestHost) As Task + Await TestVisualBasic(" +namespace N + + class [|Outer|] + end class +end namespace", UnitTestingSearchQuery.ForType("n.outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace2(host As TestHost) As Task + Await TestVisualBasic(" +namespace N + + class Outer + end class +end namespace +", UnitTestingSearchQuery.ForType("Outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace3(host As TestHost) As Task + Await TestVisualBasic(" +namespace N1.N2 + + class [|Outer|] + end class +end namespace", UnitTestingSearchQuery.ForType("N1.N2.Outer"), host) + End Function + + + Public Async Function VB_TestTypeWithNamespace4(host As TestHost) As Task + Await TestVisualBasic(" +namespace N1 + namespace N2 + + class [|Outer|] + end class + end namespace +end namespace +", UnitTestingSearchQuery.ForType("N1.N2.Outer"), host) + End Function + + + Public Async Function VB_TestMethod1(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub [|Goo|]() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod1_NoAttribute1(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + sub Goo() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod1_NoAttribute2(host As TestHost) As Task + Await TestVisualBasic(" + +class Outer + sub Goo() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod2(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub Goo() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod3(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub Goo() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + + + Public Async Function VB_TestMethod4(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub Goo(of T)() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod5(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub Goo(i as integer) + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod6(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub [|Goo|](of T)() + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod7(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub [|Goo|](a as integer) + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + + + Public Async Function VB_TestMethod8(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + + sub [|Goo|](of T)(a as integer) + end sub +end class +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=1, methodParameterCount:=1), host) + End Function + + + Public Async Function VB_TestMethod9(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + class Inner + + sub [|Goo|]() + end sub + end class +end class +", UnitTestingSearchQuery.ForMethod("Outer+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod10(host As TestHost) As Task + Await TestVisualBasic(" +class Outer + class Inner(of T) + + sub [|Goo|]() + end sub + end class +end class +", UnitTestingSearchQuery.ForMethod("Outer+Inner`1", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod11(host As TestHost) As Task + Await TestVisualBasic(" +class Outer(of T) + class Inner + + sub [|Goo|]() + end sub + end class +end class +", UnitTestingSearchQuery.ForMethod("Outer`1+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod12(host As TestHost) As Task + Await TestVisualBasic(" +class Outer(of T) + class Inner(of U) + + sub [|Goo|]() + end sub + end class +end class +", UnitTestingSearchQuery.ForMethod("Outer`1+Inner`1", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestMethod13(host As TestHost) As Task + Await TestVisualBasic(" +namespace N1.N2 + class Outer + class Inner + + sub [|Goo|]() + end sub + end class + end class +end namespace +", UnitTestingSearchQuery.ForMethod("N1.N2.Outer+Inner", "Goo", methodArity:=0, methodParameterCount:=0), host) + End Function + + + Public Async Function VB_TestExtensionMethod1(host As TestHost) As Task + Await TestVisualBasic(" +module Outer + + sub [|Goo|](i as integer) + end sub +end module +", UnitTestingSearchQuery.ForMethod("Outer", "Goo", methodArity:=0, methodParameterCount:=1), host) + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/Test2/Workspaces/TryFindSourceDefinitionTests.vb b/src/EditorFeatures/Test2/Workspaces/TryFindSourceDefinitionTests.vb index d2c16ad829b56..511978b50f4e0 100644 --- a/src/EditorFeatures/Test2/Workspaces/TryFindSourceDefinitionTests.vb +++ b/src/EditorFeatures/Test2/Workspaces/TryFindSourceDefinitionTests.vb @@ -6,6 +6,7 @@ Imports System.Threading Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.FindSymbols +Imports Microsoft.CodeAnalysis.Host Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces @@ -21,6 +22,12 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces _outputHelper = outputHelper End Sub + Private Function CreateWorkspace(definition As XElement) As TestWorkspace + Dim workspace = TestWorkspace.Create(definition, composition:=EditorTestCompositions.EditorFeatures.AddParts(GetType(WorkspaceTestLogger))) + workspace.Services.SolutionServices.SetWorkspaceTestOutput(_outputHelper) + Return workspace + End Function + Public Async Function TestFindTypeInCSharpToVisualBasicProject() As Task Dim workspaceDefinition = @@ -46,9 +53,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) - + Using workspace = CreateWorkspace(workspaceDefinition) Dim snapshot = workspace.CurrentSolution Dim Type = (Await GetProject(snapshot, "CSharpAssembly").GetCompilationAsync()).GlobalNamespace.GetTypeMembers("CSClass").Single() @@ -92,8 +97,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = CreateWorkspace(workspaceDefinition) Dim snapshot = workspace.CurrentSolution Dim Type = (Await GetProject(snapshot, "VBAssembly").GetCompilationAsync()).GlobalNamespace.GetTypeMembers("VBClass").Single() @@ -133,8 +137,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = CreateWorkspace(workspaceDefinition) Dim compilation = Await GetProject(workspace.CurrentSolution, "VBAssembly").GetCompilationAsync() Dim member = compilation.GlobalNamespace.GetMembers("N").Single().GetTypeMembers("CSClass").Single().GetMembers("M").Single() @@ -171,8 +174,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = CreateWorkspace(workspaceDefinition) Dim compilation = Await GetProject(workspace.CurrentSolution, "VBAssembly").GetCompilationAsync() Dim member = compilation.GlobalNamespace.GetMembers("N").Single().GetTypeMembers("CSClass").Single().GetMembers("M").Single() @@ -209,8 +211,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = CreateWorkspace(workspaceDefinition) Dim compilation = Await GetProject(workspace.CurrentSolution, "VBAssembly").GetCompilationAsync() Dim member = compilation.GlobalNamespace.GetMembers("N").Single().GetTypeMembers("CSClass").Single().GetMembers("M").Single() @@ -244,8 +245,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces - Using workspace = TestWorkspace.Create(workspaceDefinition) - workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) + Using workspace = CreateWorkspace(workspaceDefinition) Dim retargetedCompilation = Await GetProject(workspace.CurrentSolution, "CSharpAssembly").GetCompilationAsync() Dim originalClass = retargetedCompilation.GlobalNamespace.GetMembers("N").Single().GetTypeMembers("CSClass").Single() Dim retargetingCompilation = Await GetProject(workspace.CurrentSolution, "CSharpAssembly2").GetCompilationAsync() diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs index 3c2c02f28aaa5..7f91ae63c3544 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs @@ -96,7 +96,7 @@ public override string ToString() { "operator" => "Operators", "operator - overloaded" => "OverloadedOperators", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; switch (Text) diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractArgumentProviderTests`1.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractArgumentProviderTests`1.cs index be3c4bf9e4954..12f336f59d41d 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractArgumentProviderTests`1.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractArgumentProviderTests`1.cs @@ -52,7 +52,7 @@ private protected async Task VerifyDefaultValueAsync( { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(markup, ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(markup, GetComposition()); var code = workspaceFixture.Target.Code; var position = workspaceFixture.Target.Position; diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 2134574be5b31..142afcd08aa5c 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -45,7 +45,6 @@ public abstract class AbstractCompletionProviderTests : TestB private readonly TestFixtureHelper _fixtureHelper = new(); protected readonly Mock MockCompletionSession; - private ExportProvider _lazyExportProvider; protected bool? ShowTargetTypedCompletionFilter { get; set; } protected bool? TypeImportCompletionFeatureFlag { get; set; } @@ -91,9 +90,6 @@ private CompletionOptions GetCompletionOptions() return options; } - protected ExportProvider ExportProvider - => _lazyExportProvider ??= GetComposition().ExportProviderFactory.CreateExportProvider(); - protected virtual TestComposition GetComposition() => s_baseComposition.AddParts(GetCompletionProviderType()); @@ -111,7 +107,7 @@ protected static async Task CanUseSpeculativeSemanticModelAsync(Document d internal virtual CompletionService GetCompletionService(Project project) { var completionService = project.Services.GetRequiredService(); - var completionProviders = completionService.GetTestAccessor().GetAllProviders(ImmutableHashSet.Empty, project); + var completionProviders = completionService.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet.Empty); var completionProvider = Assert.Single(completionProviders); Assert.IsType(GetCompletionProviderType(), completionProvider); @@ -254,7 +250,7 @@ private async Task VerifyAsync( { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(markup, ExportProvider); + var workspace = workspaceFixture.Target.GetWorkspace(markup, GetComposition()); var code = workspaceFixture.Target.Code; var position = workspaceFixture.Target.Position; @@ -273,7 +269,7 @@ await VerifyWorkerAsync( protected async Task GetCompletionListAsync(string markup, string workspaceKind = null) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - var workspace = workspaceFixture.Target.GetWorkspace(markup, ExportProvider, workspaceKind: workspaceKind); + var workspace = workspaceFixture.Target.GetWorkspace(markup, GetComposition(), workspaceKind: workspaceKind); // Set options that are not CompletionOptions NonCompletionOptions?.SetGlobalOptions(workspace.GlobalOptions); @@ -288,7 +284,7 @@ protected async Task GetCompletionListAsync(string markup, strin protected async Task VerifyCustomCommitProviderAsync(string markupBeforeCommit, string itemToCommit, string expectedCodeAfterCommit, SourceCodeKind? sourceCodeKind = null, char? commitChar = null) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - using (workspaceFixture.Target.GetWorkspace(markupBeforeCommit, ExportProvider)) + using (workspaceFixture.Target.GetWorkspace(markupBeforeCommit, GetComposition())) { var code = workspaceFixture.Target.Code; var position = workspaceFixture.Target.Position; @@ -314,7 +310,7 @@ protected async Task VerifyProviderCommitAsync( { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - workspaceFixture.Target.GetWorkspace(markupBeforeCommit, ExportProvider); + workspaceFixture.Target.GetWorkspace(markupBeforeCommit, GetComposition()); var code = workspaceFixture.Target.Code; var position = workspaceFixture.Target.Position; @@ -420,7 +416,7 @@ private protected virtual async Task VerifyWorkerAsync( { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - workspaceFixture.Target.GetWorkspace(ExportProvider); + workspaceFixture.Target.GetWorkspace(GetComposition()); var document1 = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind); await CheckResultsAsync( @@ -805,7 +801,7 @@ protected static string CreateMarkupForSingleProject( private async Task VerifyItemWithReferenceWorkerAsync( string xmlString, string expectedItem, int expectedSymbols) { - using (var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider)) + using (var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition())) { var position = testWorkspace.Documents.Single(d => d.Name == "SourceDocument").CursorPosition.Value; var solution = testWorkspace.CurrentSolution; @@ -863,7 +859,7 @@ protected async Task VerifyItemWithMscorlib45Async(string markup, string expecte private async Task VerifyItemWithMscorlib45WorkerAsync( string xmlString, string expectedItem, string expectedDescription) { - using (var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider)) + using (var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition())) { var position = testWorkspace.Documents.Single(d => d.Name == "SourceDocument").CursorPosition.Value; var solution = testWorkspace.CurrentSolution; @@ -894,7 +890,7 @@ private static string GetExpectedOverloadSubstring(int expectedSymbols) protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expectedItem, string expectedDescription) { - using (var testWorkspace = TestWorkspace.Create(xmlString, exportProvider: ExportProvider)) + using (var testWorkspace = TestWorkspace.Create(xmlString, composition: GetComposition())) { var position = testWorkspace.Documents.First().CursorPosition.Value; var solution = testWorkspace.CurrentSolution; @@ -1125,7 +1121,7 @@ protected async Task VerifyCommitCharactersAsync(string initialMarkup, string te { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - workspaceFixture.Target.GetWorkspace(markup, ExportProvider); + workspaceFixture.Target.GetWorkspace(markup, GetComposition()); var code = workspaceFixture.Target.Code; var position = workspaceFixture.Target.Position; var document = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind); diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 40f50ff9c29c0..3133644f1d5bc 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -107,7 +107,7 @@ public async Task WaitForTags() await _listenerProvider.WaitAllDispatcherOperationAndTasksAsync( _workspace, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.Classification); 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/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/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 4ae7cd6b4b0fb..a46956fb6aa57 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -13,28 +13,30 @@ using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests; -using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; +using Microsoft.CodeAnalysis.LanguageServer.UnitTests; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Nerdbank.Streams; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Roslyn.Utilities; using StreamJsonRpc; using Xunit; +using Xunit.Abstractions; +using static Microsoft.CodeAnalysis.Editor.UnitTests.NavigateTo.AbstractNavigateToTests; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Roslyn.Test.Utilities @@ -42,11 +44,15 @@ namespace Roslyn.Test.Utilities [UseExportProvider] public abstract partial class AbstractLanguageServerProtocolTests { + private protected readonly ILspServiceLogger TestOutputLspLogger; + protected AbstractLanguageServerProtocolTests(ITestOutputHelper? testOutputHelper) + { + TestOutputLspLogger = testOutputHelper != null ? new TestOutputLspLogger(testOutputHelper) : NoOpLspLogger.Instance; + } + private static readonly TestComposition s_composition = EditorTestCompositions.LanguageServerProtocol .AddParts(typeof(TestDocumentTrackingService)) - .AddParts(typeof(TestWorkspaceRegistrationService)) - .AddParts(typeof(TestWorkspaceConfigurationService)) - .RemoveParts(typeof(MockWorkspaceEventListenerProvider)); + .AddParts(typeof(TestWorkspaceRegistrationService)); private class TestSpanMapperProvider : IDocumentServiceProvider { @@ -99,7 +105,7 @@ protected class OrderLocations : Comparer protected virtual TestComposition Composition => s_composition; - private protected virtual TestAnalyzerReferenceByLanguage TestAnalyzerReferences + private protected virtual TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() => new(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); protected static LSP.ClientCapabilities CapabilitiesWithVSExtensions => new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; @@ -300,22 +306,10 @@ private protected Task CreateVisualBasicTestLspServerAsync(string private Task CreateTestLspServerAsync(string[] markups, string languageName, InitializationOptions? initializationOptions) { var lspOptions = initializationOptions ?? new InitializationOptions(); - var exportProvider = Composition.ExportProviderFactory.CreateExportProvider(); - var workspaceConfigurationService = exportProvider.GetExportedValue(); - workspaceConfigurationService.Options = new WorkspaceConfigurationOptions(EnableOpeningSourceGeneratedFiles: true); - if (lspOptions.OptionUpdater != null) - { - var globalOptions = exportProvider.GetExportedValue(); - lspOptions.OptionUpdater(globalOptions); - } + var workspace = CreateWorkspace(lspOptions, workspaceKind: null); - var workspace = languageName switch - { - LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, lspOptions.SourceGeneratedMarkups, exportProvider: exportProvider), - LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, lspOptions.SourceGeneratedMarkups, exportProvider: exportProvider), - _ => throw new ArgumentException($"language name {languageName} is not valid for a test workspace"), - }; + workspace.InitializeDocuments(TestWorkspace.CreateWorkspaceElement(languageName, files: markups, sourceGeneratedFiles: lspOptions.SourceGeneratedMarkups), openDocuments: false); return CreateTestLspServerAsync(workspace, lspOptions); } @@ -332,7 +326,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) @@ -341,7 +335,7 @@ private async Task CreateTestLspServerAsync(TestWorkspace workspa solution = solution.WithProjectFilePath(project.Id, GetDocumentFilePathFromName(project.FilePath)); } - solution = solution.WithAnalyzerReferences(new[] { TestAnalyzerReferences }); + solution = solution.WithAnalyzerReferences(new[] { CreateTestAnalyzersReference() }); workspace.ChangeSolution(solution); // Important: We must wait for workspace creation operations to finish. @@ -349,7 +343,7 @@ private async Task CreateTestLspServerAsync(TestWorkspace workspa // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return await TestLspServer.CreateAsync(workspace, initializationOptions); + return await TestLspServer.CreateAsync(workspace, initializationOptions, TestOutputLspLogger); } private protected async Task CreateXmlTestLspServerAsync( @@ -358,21 +352,31 @@ private protected async Task CreateXmlTestLspServerAsync( InitializationOptions? initializationOptions = null) { var lspOptions = initializationOptions ?? new InitializationOptions(); - var exportProvider = Composition.ExportProviderFactory.CreateExportProvider(); - if (lspOptions.OptionUpdater != null) - { - var globalOptions = exportProvider.GetExportedValue(); - lspOptions.OptionUpdater(globalOptions); - } - var workspace = TestWorkspace.Create(XElement.Parse(xmlContent), openDocuments: false, exportProvider: exportProvider, workspaceKind: workspaceKind); - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { TestAnalyzerReferences })); + var workspace = CreateWorkspace(lspOptions, workspaceKind); + + workspace.InitializeDocuments(XElement.Parse(xmlContent), openDocuments: false); + workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { CreateTestAnalyzersReference() })); // Important: We must wait for workspace creation operations to finish. // Otherwise we could have a race where workspace change events triggered by creation are changing the state // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return await TestLspServer.CreateAsync(workspace, lspOptions); + return await TestLspServer.CreateAsync(workspace, lspOptions, TestOutputLspLogger); + } + + internal TestWorkspace CreateWorkspace(InitializationOptions? options, string? workspaceKind) + { + var workspace = new TestWorkspace(Composition, workspaceKind, configurationOptions: new WorkspaceConfigurationOptions(EnableOpeningSourceGeneratedFiles: true)); + options?.OptionUpdater?.Invoke(workspace.GetService()); + + workspace.GetService().Register(workspace); + + // solution crawler is currently required in order to create incremental analyzer that provides diagnostics + var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService(); + solutionCrawlerRegistrationService.Register(workspace); + + return workspace; } /// @@ -396,8 +400,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); } @@ -476,23 +486,24 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U } }; - internal sealed class TestLspServer : IDisposable + internal sealed class TestLspServer : IAsyncDisposable { public readonly TestWorkspace TestWorkspace; private readonly Dictionary> _locations; - private readonly LanguageServerTarget _languageServer; private readonly JsonRpc _clientRpc; + private readonly RoslynLanguageServer LanguageServer; + public LSP.ClientCapabilities ClientCapabilities { get; } - private TestLspServer(TestWorkspace testWorkspace, Dictionary> locations, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind) + private TestLspServer(TestWorkspace testWorkspace, Dictionary> locations, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind, ILspServiceLogger logger) { TestWorkspace = testWorkspace; ClientCapabilities = clientCapabilities; _locations = locations; var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - _languageServer = CreateLanguageServer(serverStream, serverStream, TestWorkspace, serverKind); + LanguageServer = CreateLanguageServer(serverStream, serverStream, TestWorkspace, serverKind, logger); _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, CreateJsonMessageFormatter())) { @@ -506,14 +517,14 @@ private TestLspServer( TestWorkspace testWorkspace, Dictionary> locations, LSP.ClientCapabilities clientCapabilities, - LanguageServerTarget target, + RoslynLanguageServer target, Stream clientStream) { TestWorkspace = testWorkspace; ClientCapabilities = clientCapabilities; _locations = locations; - _languageServer = target; + LanguageServer = target; _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, CreateJsonMessageFormatter())) { @@ -538,10 +549,10 @@ private static JsonMessageFormatter CreateJsonMessageFormatter() return messageFormatter; } - internal static async Task CreateAsync(TestWorkspace testWorkspace, InitializationOptions initializationOptions) + internal static async Task CreateAsync(TestWorkspace testWorkspace, InitializationOptions initializationOptions, ILspServiceLogger logger) { var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); - var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, initializationOptions.ServerKind); + var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, initializationOptions.ServerKind, logger); await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams { @@ -551,7 +562,7 @@ internal static async Task CreateAsync(TestWorkspace testWorkspac return server; } - internal static async Task CreateAsync(TestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, LanguageServerTarget target, Stream clientStream) + internal static async Task CreateAsync(TestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream) { var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); var server = new TestLspServer(testWorkspace, locations, clientCapabilities, target, clientStream); @@ -564,9 +575,8 @@ internal static async Task CreateAsync(TestWorkspace testWorkspac return server; } - private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace, WellKnownLspServerKinds serverKind) + private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace, WellKnownLspServerKinds serverKind, ILspServiceLogger logger) { - var listenerProvider = workspace.ExportProvider.GetExportedValue(); var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var servicesProvider = workspace.ExportProvider.GetExportedValue(); @@ -575,11 +585,10 @@ private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Str ExceptionStrategy = ExceptionProcessing.ISerializable, }; - var languageServer = new LanguageServerTarget( + var languageServer = new RoslynLanguageServer( servicesProvider, jsonRpc, capabilitiesProvider, - listenerProvider, - NoOpLspLogger.Instance, + logger, ProtocolConstants.RoslynLspLanguages, serverKind); @@ -589,6 +598,7 @@ private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Str public async Task ExecuteRequestAsync(string methodName, RequestType request, CancellationToken cancellationToken) where RequestType : class { + // If creating the LanguageServer threw we might timeout without this. var result = await _clientRpc.InvokeWithParameterObjectAsync(methodName, request, cancellationToken: cancellationToken).ConfigureAwait(false); return result; } @@ -639,6 +649,20 @@ public Task CloseDocumentAsync(Uri documentUri) return ExecuteRequestAsync(LSP.Methods.TextDocumentDidCloseName, didCloseParams, CancellationToken.None); } + public async Task ShutdownTestServerAsync() + { + await _clientRpc.InvokeAsync(LSP.Methods.ShutdownName).ConfigureAwait(false); + } + + public async Task ExitTestServerAsync() + { + // Since exit is a notification that disposes of the json rpc stream we cannot wait on the result + // of the request itself since it will throw a ConnectionLostException. + // Instead we wait for the server's exit task to be completed. + await _clientRpc.NotifyAsync(LSP.Methods.ExitName).ConfigureAwait(false); + await LanguageServer.WaitForExitAsync().ConfigureAwait(false); + } + public IList GetLocations(string locationName) => _locations[locationName]; public Solution GetCurrentSolution() => TestWorkspace.CurrentSolution; @@ -648,31 +672,40 @@ internal async Task WaitForDiagnosticsAsync() var listenerProvider = TestWorkspace.GetService(); await listenerProvider.GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync(); - await listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + await listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); await listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync(); } - internal RequestExecutionQueue.TestAccessor GetQueueAccessor() => _languageServer.GetTestAccessor().GetQueueAccessor(); + internal RequestExecutionQueue.TestAccessor? GetQueueAccessor() => LanguageServer.GetTestAccessor().GetQueueAccessor(); internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => GetRequiredLspService().GetTestAccessor(); internal LspWorkspaceManager GetManager() => GetRequiredLspService(); - internal LanguageServerTarget.TestAccessor GetServerAccessor() => _languageServer.GetTestAccessor(); + internal AbstractLanguageServer.TestAccessor GetServerAccessor() => LanguageServer.GetTestAccessor(); - internal T GetRequiredLspService() where T : class, ILspService => _languageServer.GetTestAccessor().GetRequiredLspService(); + internal T GetRequiredLspService() where T : class, ILspService => LanguageServer.GetTestAccessor().GetRequiredLspService(); internal ImmutableArray GetTrackedTexts() => GetManager().GetTrackedLspText().Values.ToImmutableArray(); - public void Dispose() + public async ValueTask DisposeAsync() { - // Some tests manually call shutdown, so avoid calling shutdown twice if already called. - if (!_languageServer.HasShutdownStarted) + TestWorkspace.GetService().Deregister(TestWorkspace); + + var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)TestWorkspace.Services.GetRequiredService(); + solutionCrawlerRegistrationService.Unregister(TestWorkspace); + + // Some tests will manually call shutdown and exit, so attempting to call this during dispose + // will fail as the server's jsonrpc instance will be disposed of. + if (!LanguageServer.GetTestAccessor().HasShutdownStarted()) { - _languageServer.GetTestAccessor().ShutdownServer(); + await ShutdownTestServerAsync(); + await ExitTestServerAsync(); } - _languageServer.GetTestAccessor().ExitServer(); + // Wait for all the exit notifications to run to completion. + await LanguageServer.WaitForExitAsync(); + TestWorkspace.Dispose(); _clientRpc.Dispose(); } diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs b/src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs new file mode 100644 index 0000000000000..22796b723e10f --- /dev/null +++ b/src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs @@ -0,0 +1,37 @@ +// 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.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; +public class TestOutputLspLogger : ILspServiceLogger +{ + private readonly ITestOutputHelper _testOutputHelper; + public TestOutputLspLogger(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + public void LogEndContext(string message, params object[] @params) => Log("End", message, @params); + + public void LogError(string message, params object[] @params) => Log("Error", message, @params); + + public void LogException(Exception exception, string? message = null, params object[] @params) + => Log("Warning", $"{message}{Environment.NewLine}{exception}", @params); + + public void LogInformation(string message, params object[] @params) => Log("Info", message, @params); + + public void LogStartContext(string message, params object[] @params) => Log("Start", message, @params); + + public void LogWarning(string message, params object[] @params) => Log("Warning", message, @params); + + private void Log(string level, string message, params object[] @params) + => _testOutputHelper.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}][{level}]{message}", @params); +} diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs b/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs index 1257a37ef8dbf..7ae652d58f9e3 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs @@ -66,7 +66,7 @@ public abstract class AbstractNavigateToTests internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousSubstring, true, false, ImmutableArray.Empty); internal static readonly PatternMatch s_emptyFuzzyPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Fuzzy, true, false, ImmutableArray.Empty); - protected abstract TestWorkspace CreateWorkspace(string content, ExportProvider exportProvider); + protected abstract TestWorkspace CreateWorkspace(string content, TestComposition composition); protected abstract string Language { get; } public enum Composition @@ -130,9 +130,9 @@ private protected TestWorkspace CreateWorkspace( TestHost testHost, TestComposition composition) { - var exportProvider = composition.WithTestHostParts(testHost).ExportProviderFactory.CreateExportProvider(); + composition = composition.WithTestHostParts(testHost); - var workspace = TestWorkspace.Create(workspaceElement, exportProvider: exportProvider); + var workspace = TestWorkspace.Create(workspaceElement, composition: composition); InitializeWorkspace(workspace); return workspace; } @@ -142,9 +142,9 @@ private protected TestWorkspace CreateWorkspace( TestHost testHost, TestComposition composition) { - var exportProvider = composition.WithTestHostParts(testHost).ExportProviderFactory.CreateExportProvider(); + composition = composition.WithTestHostParts(testHost); - var workspace = CreateWorkspace(content, exportProvider); + var workspace = CreateWorkspace(content, composition); InitializeWorkspace(workspace); return workspace; } diff --git a/src/EditorFeatures/TestUtilities/QuickInfo/ToolTipAssert.cs b/src/EditorFeatures/TestUtilities/QuickInfo/ToolTipAssert.cs index 480a0290961d0..9d7d81393bea8 100644 --- a/src/EditorFeatures/TestUtilities/QuickInfo/ToolTipAssert.cs +++ b/src/EditorFeatures/TestUtilities/QuickInfo/ToolTipAssert.cs @@ -48,7 +48,7 @@ public static void EqualContent(object expected, object actual) return; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (Exception) { @@ -197,7 +197,7 @@ private static void ContainerToString(object element, string indent, StringBuild return; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static string ContainerStyleToString(ContainerElementStyle style) 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/Utilities/CSharpCodeActionOptions.cs b/src/EditorFeatures/TestUtilities/Utilities/CSharpCodeActionOptions.cs index 9766f13ab2af8..ebe0d25a3444e 100644 --- a/src/EditorFeatures/TestUtilities/Utilities/CSharpCodeActionOptions.cs +++ b/src/EditorFeatures/TestUtilities/Utilities/CSharpCodeActionOptions.cs @@ -20,10 +20,14 @@ namespace Microsoft.CodeAnalysis.Test.Utilities; internal static class CSharpCodeActionOptions { - public static CodeActionOptions Default = new( - new CodeCleanupOptions( - CSharpSyntaxFormattingOptions.Default, - CSharpSimplifierOptions.Default), - CSharpCodeGenerationOptions.Default, - CSharpIdeCodeStyleOptions.Default); + public static CodeActionOptions Default = new() + { + CleanupOptions = new() + { + FormattingOptions = CSharpSyntaxFormattingOptions.Default, + SimplifierOptions = CSharpSimplifierOptions.Default + }, + CodeGenerationOptions = CSharpCodeGenerationOptions.Default, + CodeStyleOptions = CSharpIdeCodeStyleOptions.Default + }; } diff --git a/src/EditorFeatures/TestUtilities/Utilities/VisualBasicCodeActionOptions.cs b/src/EditorFeatures/TestUtilities/Utilities/VisualBasicCodeActionOptions.cs index c6be069c144d4..5c48aee7ff262 100644 --- a/src/EditorFeatures/TestUtilities/Utilities/VisualBasicCodeActionOptions.cs +++ b/src/EditorFeatures/TestUtilities/Utilities/VisualBasicCodeActionOptions.cs @@ -21,12 +21,16 @@ namespace Microsoft.CodeAnalysis.Test.Utilities; internal static class VisualBasicCodeActionOptions { - public static CodeActionOptions Default = new( - new CodeCleanupOptions( - VisualBasicSyntaxFormattingOptions.Default, - VisualBasicSimplifierOptions.Default), - VisualBasicCodeGenerationOptions.Default, - VisualBasicIdeCodeStyleOptions.Default); + public static CodeActionOptions Default = new() + { + CleanupOptions = new() + { + FormattingOptions = VisualBasicSyntaxFormattingOptions.Default, + SimplifierOptions = VisualBasicSimplifierOptions.Default, + }, + CodeGenerationOptions = VisualBasicCodeGenerationOptions.Default, + CodeStyleOptions = VisualBasicIdeCodeStyleOptions.Default + }; public static CodeActionOptions WithWrappingColumn(this CodeActionOptions options, int value) => options with { WrappingColumn = value }; diff --git a/src/EditorFeatures/TestUtilities/Workspaces/CSharpTestWorkspaceFixture.cs b/src/EditorFeatures/TestUtilities/Workspaces/CSharpTestWorkspaceFixture.cs index 5c99107277314..8bcd66fbaaa69 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/CSharpTestWorkspaceFixture.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/CSharpTestWorkspaceFixture.cs @@ -6,18 +6,20 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Composition; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces { public class CSharpTestWorkspaceFixture : TestWorkspaceFixture { - protected override TestWorkspace CreateWorkspace(ExportProvider exportProvider = null) + protected override TestWorkspace CreateWorkspace(TestComposition composition = null) { - return TestWorkspace.CreateCSharp2( - new string[] { string.Empty, }, - new CSharpParseOptions[] { new CSharpParseOptions(kind: SourceCodeKind.Regular), }, - exportProvider: exportProvider); + return TestWorkspace.CreateWithSingleEmptySourceFile( + LanguageNames.CSharp, + compilationOptions: null, + parseOptions: new CSharpParseOptions(kind: SourceCodeKind.Regular), + composition: composition); } } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/InteractiveCSharpTestWorkspaceFixture.cs b/src/EditorFeatures/TestUtilities/Workspaces/InteractiveCSharpTestWorkspaceFixture.cs index 9d65546ecccbe..c744b9ab7435c 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/InteractiveCSharpTestWorkspaceFixture.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/InteractiveCSharpTestWorkspaceFixture.cs @@ -5,25 +5,28 @@ #nullable disable using System.Xml.Linq; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Composition; +using static Microsoft.CodeAnalysis.Editor.UnitTests.NavigateTo.AbstractNavigateToTests; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces { public class InteractiveCSharpTestWorkspaceFixture : CSharpTestWorkspaceFixture { - internal static TestWorkspace CreateInteractiveWorkspace(string fileContent, ExportProvider exportProvider) + internal static TestWorkspace CreateInteractiveWorkspace(string fileContent, TestComposition composition) { var workspaceDefinition = $@" - + "; - return TestWorkspace.Create(XElement.Parse(workspaceDefinition), exportProvider: exportProvider, workspaceKind: WorkspaceKind.Interactive); + return TestWorkspace.Create(XElement.Parse(workspaceDefinition), composition: composition, workspaceKind: WorkspaceKind.Interactive); } - protected override TestWorkspace CreateWorkspace(ExportProvider exportProvider = null) - => CreateInteractiveWorkspace(fileContent: "", exportProvider); + protected override TestWorkspace CreateWorkspace(TestComposition composition = null) + => CreateInteractiveWorkspace(fileContent: "", composition); } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 5ac6703cd3872..0fcd99de79fc3 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,15 @@ internal TestDocumentLoader(TestHostDocument hostDocument, string text) _text = text; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create(), _hostDocument.FilePath)); + internal override string? FilePath + => _hostDocument.FilePath; + + internal 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; + public IWpfTextView GetTextView() { if (_textView == null) @@ -352,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(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, isGenerated: false) + .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.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs index 6c43c13519a35..0378d67672219 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.ServiceModel.Syndication; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -21,6 +23,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.CodeAnalysis.UnitTests; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -35,6 +38,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces public partial class TestWorkspace : Workspace { public ExportProvider ExportProvider { get; } + public TestComposition? Composition { get; } public bool CanApplyChangeDocument { get; set; } @@ -56,21 +60,30 @@ public partial class TestWorkspace : Workspace private readonly Dictionary _createdTextBuffers = new(); private readonly string _workspaceKind; - public TestWorkspace( - ExportProvider? exportProvider = null, + internal TestWorkspace( TestComposition? composition = null, string? workspaceKind = WorkspaceKind.Host, Guid solutionTelemetryId = default, bool disablePartialSolutions = true, - bool ignoreUnchangeableDocumentsWhenApplyingChanges = true) - : base(GetHostServices(exportProvider, composition), workspaceKind ?? WorkspaceKind.Host) + bool ignoreUnchangeableDocumentsWhenApplyingChanges = true, + WorkspaceConfigurationOptions? configurationOptions = null) + : base(GetHostServices(ref composition, configurationOptions != null), workspaceKind ?? WorkspaceKind.Host) { - Contract.ThrowIfTrue(exportProvider != null && composition != null); + this.Composition = composition; + this.ExportProvider = composition.ExportProviderFactory.CreateExportProvider(); + + var partialSolutionsTestHook = Services.GetRequiredService(); + partialSolutionsTestHook.IsPartialSolutionDisabled = disablePartialSolutions; + + // configure workspace before creating any solutions: + if (configurationOptions != null) + { + var workspaceConfigurationService = GetService(); + workspaceConfigurationService.Options = new WorkspaceConfigurationOptions(EnableOpeningSourceGeneratedFiles: true); + } SetCurrentSolution(CreateSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create()).WithTelemetryId(solutionTelemetryId))); - this.TestHookPartialSolutionsDisabled = disablePartialSolutions; - this.ExportProvider = exportProvider ?? GetComposition(composition).ExportProviderFactory.CreateExportProvider(); _workspaceKind = workspaceKind ?? WorkspaceKind.Host; this.Projects = new List(); this.Documents = new List(); @@ -109,11 +122,17 @@ public TestWorkspace( _metadataAsSourceFileService = ExportProvider.GetExportedValues().FirstOrDefault(); } - internal static TestComposition GetComposition(TestComposition? composition) - => composition ?? EditorTestCompositions.EditorFeatures; + private static HostServices GetHostServices([NotNull] ref TestComposition? composition, bool hasWorkspaceConfigurationOptions) + { + composition ??= EditorTestCompositions.EditorFeatures; - private static HostServices GetHostServices(ExportProvider? exportProvider = null, TestComposition? composition = null) - => (exportProvider != null) ? VisualStudioMefHostServices.Create(exportProvider) : GetComposition(composition).GetHostServices(); + if (hasWorkspaceConfigurationOptions) + { + composition = composition.AddParts(typeof(TestWorkspaceConfigurationService)); + } + + return composition.GetHostServices(); + } protected internal override bool PartialSemanticsEnabled { diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspaceFixture.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspaceFixture.cs index 8c8e99ead8b56..5d02669924b3b 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspaceFixture.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspaceFixture.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Xml; using System.Xml.Linq; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; @@ -25,13 +26,13 @@ public abstract class TestWorkspaceFixture : IDisposable public TestHostDocument CurrentDocument => _currentDocument ?? _workspace.Documents.Single(); - public TestWorkspace GetWorkspace(ExportProvider exportProvider = null) + public TestWorkspace GetWorkspace(TestComposition composition = null) { - _workspace = _workspace ?? CreateWorkspace(exportProvider); + _workspace ??= CreateWorkspace(composition); return _workspace; } - public TestWorkspace GetWorkspace(string markup, ExportProvider exportProvider = null, string workspaceKind = null) + public TestWorkspace GetWorkspace(string markup, TestComposition composition = null, string workspaceKind = null) { // If it looks like XML, we'll treat it as XML; any parse error would be rejected and will throw. // We'll do a case insensitive search here so if somebody has a lowercase W it'll be tried (and @@ -41,7 +42,7 @@ public TestWorkspace GetWorkspace(string markup, ExportProvider exportProvider = CloseTextView(); _workspace?.Dispose(); - _workspace = TestWorkspace.CreateWorkspace(XElement.Parse(markup), exportProvider: exportProvider, workspaceKind: workspaceKind); + _workspace = TestWorkspace.CreateWorkspace(XElement.Parse(markup), composition: composition, workspaceKind: workspaceKind); _currentDocument = _workspace.Documents.First(d => d.CursorPosition.HasValue); Position = _currentDocument.CursorPosition.Value; Code = _currentDocument.GetTextBuffer().CurrentSnapshot.GetText(); @@ -50,13 +51,13 @@ public TestWorkspace GetWorkspace(string markup, ExportProvider exportProvider = else { MarkupTestFile.GetPosition(markup.NormalizeLineEndings(), out Code, out Position); - var workspace = GetWorkspace(exportProvider); + var workspace = GetWorkspace(composition); _currentDocument = workspace.Documents.Single(); return workspace; } } - protected abstract TestWorkspace CreateWorkspace(ExportProvider exportProvider); + protected abstract TestWorkspace CreateWorkspace(TestComposition composition); public void Dispose() { diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_Create.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_Create.cs index 1c94ed978e938..f7b603a7d641b 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_Create.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_Create.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Composition; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces { @@ -103,16 +104,6 @@ internal static TestWorkspace Create( return Create(workspaceKind, language, compilationOptions, parseOptions, new[] { content }); } - /// Can pass in multiple file contents: files will be named test1.cs, test2.cs, etc. - internal static TestWorkspace Create( - string language, - CompilationOptions compilationOptions, - ParseOptions parseOptions, - params string[] files) - { - return Create(language, compilationOptions, parseOptions, files, exportProvider: null); - } - /// Can pass in multiple file contents: files will be named test1.cs, test2.cs, etc. internal static TestWorkspace Create( string workspaceKind, @@ -121,7 +112,7 @@ internal static TestWorkspace Create( ParseOptions parseOptions, params string[] files) { - return Create(language, compilationOptions, parseOptions, files, exportProvider: null, workspaceKind: workspaceKind); + return Create(language, compilationOptions, parseOptions, files, workspaceKind: workspaceKind); } internal static string GetDefaultTestSourceDocumentName(int index, string extension) @@ -133,7 +124,6 @@ internal static TestWorkspace Create( ParseOptions parseOptions, string[] files, string[] sourceGeneratedFiles = null, - ExportProvider exportProvider = null, TestComposition composition = null, string[] metadataReferences = null, string workspaceKind = null, @@ -144,48 +134,42 @@ internal static TestWorkspace Create( IDocumentServiceProvider documentServiceProvider = null) { var workspaceElement = CreateWorkspaceElement(language, compilationOptions, parseOptions, files, sourceGeneratedFiles, metadataReferences, extension, commonReferences, isMarkup); - return Create(workspaceElement, openDocuments, exportProvider, composition, workspaceKind, documentServiceProvider); + return Create(workspaceElement, openDocuments, composition, workspaceKind, documentServiceProvider); } - internal static TestWorkspace Create( + internal static TestWorkspace CreateWithSingleEmptySourceFile( string language, CompilationOptions compilationOptions, - ParseOptions[] parseOptions, - string[] files, - ExportProvider exportProvider) + ParseOptions parseOptions, + TestComposition composition) { - Debug.Assert(parseOptions == null || (files.Length == parseOptions.Length), "Please specify a parse option for each file."); + var documentElements = new[] + { + CreateDocumentElement(code: "", filePath: GetDefaultTestSourceDocumentName(index: 0, GetSourceFileExtension(language, parseOptions)), parseOptions) + }; - var documentElements = new List(); - var index = 0; - string extension; + var workspaceElement = CreateWorkspaceElement( + CreateProjectElement("Test", language, commonReferences: true, parseOptions, compilationOptions, documentElements)); - for (var i = 0; i < files.Length; i++) - { - if (language == LanguageNames.CSharp) - { - extension = parseOptions[i].Kind == SourceCodeKind.Regular - ? CSharpExtension - : CSharpScriptExtension; - } - else if (language == LanguageNames.VisualBasic) - { - extension = parseOptions[i].Kind == SourceCodeKind.Regular - ? VisualBasicExtension - : VisualBasicScriptExtension; - } - else - { - extension = language; - } + return Create(workspaceElement, composition: composition); + } - documentElements.Add(CreateDocumentElement(files[i], GetDefaultTestSourceDocumentName(index++, extension), parseOptions?[i])); + private static string GetSourceFileExtension(string language, ParseOptions parseOptions) + { + if (language == LanguageNames.CSharp) + { + return parseOptions.Kind == SourceCodeKind.Regular + ? CSharpExtension + : CSharpScriptExtension; + } + else if (language == LanguageNames.VisualBasic) + { + return parseOptions.Kind == SourceCodeKind.Regular + ? VisualBasicExtension + : VisualBasicScriptExtension; } - var workspaceElement = CreateWorkspaceElement( - CreateProjectElement("Test", language, true, parseOptions.FirstOrDefault(), compilationOptions, documentElements)); - - return Create(workspaceElement, exportProvider: exportProvider); + throw ExceptionUtilities.UnexpectedValue(language); } #region C# @@ -194,13 +178,12 @@ public static TestWorkspace CreateCSharp( string file, ParseOptions parseOptions = null, CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null, TestComposition composition = null, string[] metadataReferences = null, bool isMarkup = true, bool openDocuments = false) { - return CreateCSharp(new[] { file }, Array.Empty(), parseOptions, compilationOptions, exportProvider, composition, metadataReferences, isMarkup, openDocuments); + return CreateCSharp(new[] { file }, Array.Empty(), parseOptions, compilationOptions, composition, metadataReferences, isMarkup, openDocuments); } public static TestWorkspace CreateCSharp( @@ -208,22 +191,12 @@ public static TestWorkspace CreateCSharp( string[] sourceGeneratedFiles = null, ParseOptions parseOptions = null, CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null, TestComposition composition = null, string[] metadataReferences = null, bool isMarkup = true, bool openDocuments = false) { - return Create(LanguageNames.CSharp, compilationOptions, parseOptions, files, sourceGeneratedFiles, exportProvider, composition, metadataReferences, isMarkup: isMarkup, openDocuments: openDocuments); - } - - public static TestWorkspace CreateCSharp2( - string[] files, - ParseOptions[] parseOptions = null, - CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null) - { - return Create(LanguageNames.CSharp, compilationOptions, parseOptions, files, exportProvider); + return Create(LanguageNames.CSharp, compilationOptions, parseOptions, files, sourceGeneratedFiles, composition, metadataReferences, isMarkup: isMarkup, openDocuments: openDocuments); } #endregion @@ -234,12 +207,11 @@ public static TestWorkspace CreateVisualBasic( string file, ParseOptions parseOptions = null, CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null, TestComposition composition = null, string[] metadataReferences = null, bool openDocuments = false) { - return CreateVisualBasic(new[] { file }, Array.Empty(), parseOptions, compilationOptions, exportProvider, composition, metadataReferences, openDocuments); + return CreateVisualBasic(new[] { file }, Array.Empty(), parseOptions, compilationOptions, composition, metadataReferences, openDocuments); } public static TestWorkspace CreateVisualBasic( @@ -247,22 +219,11 @@ public static TestWorkspace CreateVisualBasic( string[] sourceGeneratedFiles = null, ParseOptions parseOptions = null, CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null, TestComposition composition = null, string[] metadataReferences = null, bool openDocuments = false) { - return Create(LanguageNames.VisualBasic, compilationOptions, parseOptions, files, sourceGeneratedFiles, exportProvider, composition, metadataReferences, openDocuments: openDocuments); - } - - /// Can pass in multiple file contents with individual source kind: files will be named test1.vb, test2.vbx, etc. - public static TestWorkspace CreateVisualBasic( - string[] files, - ParseOptions[] parseOptions = null, - CompilationOptions compilationOptions = null, - ExportProvider exportProvider = null) - { - return Create(LanguageNames.VisualBasic, compilationOptions, parseOptions, files, exportProvider); + return Create(LanguageNames.VisualBasic, compilationOptions, parseOptions, files, sourceGeneratedFiles, composition, metadataReferences, openDocuments: openDocuments); } #endregion diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs index 48d8d36bfb28c..1babc09e86ae6 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs @@ -58,29 +58,27 @@ public override int GetHashCode() => RuntimeHelpers.GetHashCode(this); } - public static TestWorkspace Create(string xmlDefinition, bool openDocuments = false, ExportProvider exportProvider = null, TestComposition composition = null) - => Create(XElement.Parse(xmlDefinition), openDocuments, exportProvider, composition); + public static TestWorkspace Create(string xmlDefinition, bool openDocuments = false, TestComposition composition = null) + => Create(XElement.Parse(xmlDefinition), openDocuments, composition); public static TestWorkspace CreateWorkspace( XElement workspaceElement, bool openDocuments = true, - ExportProvider exportProvider = null, TestComposition composition = null, string workspaceKind = null) { - return Create(workspaceElement, openDocuments, exportProvider, composition, workspaceKind); + return Create(workspaceElement, openDocuments, composition, workspaceKind); } internal static TestWorkspace Create( XElement workspaceElement, bool openDocuments = true, - ExportProvider exportProvider = null, TestComposition composition = null, string workspaceKind = null, IDocumentServiceProvider documentServiceProvider = null, bool ignoreUnchangeableDocumentsWhenApplyingChanges = true) { - var workspace = new TestWorkspace(exportProvider, composition, workspaceKind, ignoreUnchangeableDocumentsWhenApplyingChanges: ignoreUnchangeableDocumentsWhenApplyingChanges); + var workspace = new TestWorkspace(composition, workspaceKind, ignoreUnchangeableDocumentsWhenApplyingChanges: ignoreUnchangeableDocumentsWhenApplyingChanges); workspace.InitializeDocuments(workspaceElement, openDocuments, documentServiceProvider); return workspace; } @@ -983,13 +981,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/TestUtilities/Workspaces/VisualBasicTestWorkspaceFixture.cs b/src/EditorFeatures/TestUtilities/Workspaces/VisualBasicTestWorkspaceFixture.cs index 2ff378469e9e5..308f63a3daed5 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/VisualBasicTestWorkspaceFixture.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/VisualBasicTestWorkspaceFixture.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.VisualStudio.Composition; @@ -16,13 +17,13 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces { public class VisualBasicTestWorkspaceFixture : TestWorkspaceFixture { - protected override TestWorkspace CreateWorkspace(ExportProvider exportProvider = null) + protected override TestWorkspace CreateWorkspace(TestComposition composition = null) { - return TestWorkspace.CreateVisualBasic( - new string[] { string.Empty }, - new VisualBasicParseOptions[] { new VisualBasicParseOptions(kind: SourceCodeKind.Regular) }, - new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary), - exportProvider: exportProvider); + return TestWorkspace.CreateWithSingleEmptySourceFile( + LanguageNames.VisualBasic, + compilationOptions: new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary), + parseOptions: new VisualBasicParseOptions(kind: SourceCodeKind.Regular), + composition: composition); } } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/WorkspaceTestLogger.cs b/src/EditorFeatures/TestUtilities/Workspaces/WorkspaceTestLogger.cs new file mode 100644 index 0000000000000..8c766d454772c --- /dev/null +++ b/src/EditorFeatures/TestUtilities/Workspaces/WorkspaceTestLogger.cs @@ -0,0 +1,37 @@ +// 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 System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.Host; + +[ExportWorkspaceService(typeof(IWorkspaceTestLogger), ServiceLayer.Host), Shared, PartNotDiscoverable] +internal sealed class WorkspaceTestLogger : IWorkspaceTestLogger +{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public WorkspaceTestLogger() + { + } + + public ITestOutputHelper? OutputHelper { get; set; } + + public void Log(string message) + => OutputHelper?.WriteLine(message); +} + +internal static class WorkspaceTestLoggerExtensions +{ + public static void SetWorkspaceTestOutput(this SolutionServices services, ITestOutputHelper outputHelper) + => Assert.IsType(services.GetRequiredService()).OutputHelper = outputHelper; +} + diff --git a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs index 10760d3d1b757..a71d45b188223 100644 --- a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs +++ b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs @@ -36,7 +36,8 @@ private class SnapshotSourceText : SourceText private readonly Encoding? _encoding; private readonly TextBufferContainer? _container; - private SnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextSnapshot editorSnapshot, TextBufferContainer container) + private SnapshotSourceText(ITextBufferCloneService? textBufferCloneService, ITextSnapshot editorSnapshot, SourceHashAlgorithm checksumAlgorithm, TextBufferContainer container) + : base(checksumAlgorithm: checksumAlgorithm) { Contract.ThrowIfNull(editorSnapshot); @@ -46,7 +47,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); @@ -82,7 +84,7 @@ public static SourceText From(ITextBufferCloneService? textBufferCloneService, I // Avoid capturing `textBufferCloneServiceOpt` on the fast path var tempTextBufferCloneService = textBufferCloneService; - snapshot = s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(tempTextBufferCloneService, s, container)); + snapshot = s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(tempTextBufferCloneService, s, SourceHashAlgorithms.OpenDocumentChecksumAlgorithm, container)); } return snapshot; @@ -99,7 +101,7 @@ internal static SourceText From(ITextBufferCloneService? textBufferCloneService, } Contract.ThrowIfFalse(editorSnapshot.TextBuffer == container.GetTextBuffer()); - return s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(textBufferCloneService, s, container)); + return s_textSnapshotMap.GetValue(editorSnapshot, s => new SnapshotSourceText(textBufferCloneService, s, SourceHashAlgorithms.OpenDocumentChecksumAlgorithm, container)); } public override Encoding? Encoding @@ -263,8 +265,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 +280,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/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb b/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb index 5d95701bf9851..3ff2273657ce8 100644 --- a/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb @@ -41,28 +41,28 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.AutomaticCompletion nextAction() End Sub - Protected Overrides Function TreatAsReturn(document As Document, caretPosition As Integer, cancellationToken As CancellationToken) As Boolean + Protected Overrides Function TreatAsReturn(document As ParsedDocument, caretPosition As Integer, cancellationToken As CancellationToken) As Boolean ' No special handling in VB. Return False End Function - Protected Overrides Sub ModifySelectedNode(args As AutomaticLineEnderCommandArgs, document As Document, selectedNode As SyntaxNode, addBrace As Boolean, caretPosition As Integer, cancellationToken As CancellationToken) + Protected Overrides Sub ModifySelectedNode(args As AutomaticLineEnderCommandArgs, document As ParsedDocument, selectedNode As SyntaxNode, addBrace As Boolean, caretPosition As Integer, cancellationToken As CancellationToken) End Sub - Protected Overrides Function GetValidNodeToModifyBraces(document As Document, caretPosition As Integer, cancellationToken As CancellationToken) As (SyntaxNode, Boolean)? + Protected Overrides Function GetValidNodeToModifyBraces(document As ParsedDocument, caretPosition As Integer, cancellationToken As CancellationToken) As (SyntaxNode, Boolean)? Return Nothing End Function - Protected Overrides Function FormatAndApplyBasedOnEndToken(document As Document, position As Integer, formattingOptions As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Document + Protected Overrides Function FormatBasedOnEndToken(document As ParsedDocument, position As Integer, formattingOptions As SyntaxFormattingOptions, cancellationToken As CancellationToken) As IList(Of TextChange) ' vb does automatic line commit ' no need to do explicit formatting - Return document + Return SpecializedCollections.EmptyList(Of TextChange) End Function - Protected Overrides Function GetEndingString(document As Document, position As Integer, cancellationToken As CancellationToken) As String + Protected Overrides Function GetEndingString(document As ParsedDocument, position As Integer) As String ' prepare expansive information from document - Dim root = document.GetSyntaxRootSynchronously(cancellationToken) - Dim text = root.SyntaxTree.GetText(cancellationToken) + Dim root = document.Root + Dim text = document.Text ' get line where the caret is on Dim line = text.Lines.GetLineFromPosition(position) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index 5aa13bef83b72..0c93051c80ae9 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -16,7 +16,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Inherits AbstractCompletionProviderTests(Of VisualBasicTestWorkspaceFixture) Protected Overrides Function CreateWorkspace(fileContents As String) As TestWorkspace - Return TestWorkspace.CreateVisualBasic(fileContents, exportProvider:=ExportProvider) + Return TestWorkspace.CreateVisualBasic(fileContents, composition:=GetComposition()) End Function Friend Overrides Function GetCompletionService(project As Project) As CompletionService @@ -111,7 +111,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Protected Async Function VerifySendEnterThroughToEditorAsync( initialMarkup As String, textTypedSoFar As String, expected As Boolean, Optional sourceCodeKind As SourceCodeKind = SourceCodeKind.Regular) As Task - Using workspace = TestWorkspace.CreateVisualBasic(initialMarkup, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.CreateVisualBasic(initialMarkup, composition:=GetComposition()) Dim hostDocument = workspace.DocumentWithCursor workspace.OnDocumentSourceCodeKindChanged(hostDocument.Id, sourceCodeKind) Dim documentId = workspace.GetDocumentId(hostDocument) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CompletionListTagCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CompletionListTagCompletionProviderTests.vb index 26dead499d387..d60e7b80dab8c 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CompletionListTagCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CompletionListTagCompletionProviderTests.vb @@ -273,8 +273,8 @@ Class C End Class ]]>.Value - Await VerifyItemExistsAsync(markup, "ColorNamespace.Color.X", glyph:=CType(Glyph.FieldPublic, Integer)) - Await VerifyItemExistsAsync(markup, "ColorNamespace.Color.Y", glyph:=CType(Glyph.PropertyPublic, Integer)) + Await VerifyItemExistsAsync(markup, "ColorNamespace.Color.X") + Await VerifyItemExistsAsync(markup, "ColorNamespace.Color.Y") End Function diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb index 18c175bd29f7d..33211c67550ec 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/CrefCompletionProviderTests.vb @@ -415,13 +415,13 @@ Class C End Sub End Class]]>.Value.NormalizeLineEndings() - Using workspace = TestWorkspace.Create(LanguageNames.VisualBasic, New VisualBasicCompilationOptions(OutputKind.ConsoleApplication), New VisualBasicParseOptions(), {text}, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.Create(LanguageNames.VisualBasic, New VisualBasicCompilationOptions(OutputKind.ConsoleApplication), New VisualBasicParseOptions(), {text}, composition:=GetComposition()) Dim called = False Dim hostDocument = workspace.DocumentWithCursor Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) Dim service = GetCompletionService(document.Project) - Dim provider = Assert.IsType(Of CrefCompletionProvider)(service.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty).Single()) + Dim provider = Assert.IsType(Of CrefCompletionProvider)(service.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty).Single()) provider.GetTestAccessor().SetSpeculativeNodeCallback( Sub(node As SyntaxNode) ' asserts that we aren't be asked speculate on nodes inside documentation trivia. diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/EnumCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/EnumCompletionProviderTests.vb index a87e6a0a10187..e901cf08dcec6 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/EnumCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/EnumCompletionProviderTests.vb @@ -374,23 +374,6 @@ End Class Await VerifyNoItemsExistAsync(markup) End Function - - Public Async Function TestLocalNoAs() As Task - Dim markup = .Value - Await VerifyItemExistsAsync(markup, "e") - Await VerifyItemIsAbsentAsync(markup, "e As E") - End Function - Public Async Function TestIncludeEnumAfterTyping() As Task Dim markup = - Using workspace = TestWorkspace.Create(element, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.Create(element, composition:=GetComposition()) Dim position = workspace.Documents.Single().CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id) Dim service = GetCompletionService(document.Project) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb index 975350744c6ae..42866af049e76 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.vb @@ -446,7 +446,7 @@ End Program - Using workspace = TestWorkspace.Create(text, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.Create(text, composition:=GetComposition()) Dim hostDocument = workspace.Documents.First() Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb index 60b228bcd001c..9f296496eb9f9 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/OverrideCompletionProviderTests.vb @@ -1847,7 +1847,7 @@ public class C - Using workspace = TestWorkspace.Create(text, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.Create(text, composition:=GetComposition()) Dim hostDocument = workspace.Documents.First() Dim caretPosition = hostDocument.CursorPosition.Value Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb index a498b983a136d..bb2a76f38ac29 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.vb @@ -362,7 +362,7 @@ End Class MarkupTestFile.GetPosition(markup.NormalizedValue, code, position) Using workspaceFixture = New VisualBasicTestWorkspaceFixture() - workspaceFixture.GetWorkspace(ExportProvider) + workspaceFixture.GetWorkspace(GetComposition()) Dim document1 = workspaceFixture.UpdateDocument(code, SourceCodeKind.Regular) Dim options As CompletionOptions @@ -390,7 +390,7 @@ End Class triggerInfo = If(triggerInfo, CompletionTrigger.CreateInsertionTrigger("a"c)) Dim service = GetCompletionService(document.Project) - Dim provider = Assert.Single(service.GetTestAccessor().GetAllProviders(ImmutableHashSet(Of String).Empty)) + Dim provider = Assert.Single(service.GetTestAccessor().GetImportedAndBuiltInProviders(ImmutableHashSet(Of String).Empty)) Dim context = Await service.GetTestAccessor().GetContextAsync( provider, document, position, triggerInfo.Value, options, CancellationToken.None) diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb index 3acb9cbcb0174..23f99a558349d 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb @@ -7651,7 +7651,7 @@ End Namespace - Using workspace = TestWorkspace.Create(input, exportProvider:=ExportProvider) + Using workspace = TestWorkspace.Create(input, composition:=GetComposition()) Dim document = workspace.CurrentSolution.GetDocument(workspace.DocumentWithCursor.Id) Dim position = workspace.DocumentWithCursor.CursorPosition.Value Await CheckResultsAsync(document, position, "InstanceMethod", expectedDescriptionOrNull:=Nothing, usePreviousCharAsTrigger:=False, checkForAbsence:=False, diff --git a/src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb b/src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb index 305251f08b509..5a406b693eca3 100644 --- a/src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb @@ -5,28 +5,48 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.ConvertTupleToStruct Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings Imports Microsoft.CodeAnalysis.Remote.Testing Imports Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct.VisualBasicConvertTupleToStructCodeRefactoringProvider) + Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertTupleToStruct + Public Class ConvertTupleToStructTests - Inherits AbstractVisualBasicCodeActionTest - Protected Overrides Function CreateCodeRefactoringProvider(workspace As Workspace, parameters As TestParameters) As CodeRefactoringProvider - Return New VisualBasicConvertTupleToStructCodeRefactoringProvider() - End Function + Private Shared Async Function TestAsync( + text As String, + expected As String, + Optional index As Integer = 0, + Optional equivalenceKey As String = Nothing, + Optional testHost As TestHost = TestHost.InProcess, + Optional actions As String() = Nothing) As Task - Protected Overrides Function MassageActions(actions As ImmutableArray(Of CodeAction)) As ImmutableArray(Of CodeAction) - Return FlattenActions(actions) + If index <> 0 Then + Assert.NotNull(equivalenceKey) + End If + + Dim test = New VerifyVB.Test With { + .TestCode = text, + .FixedCode = expected, + .TestHost = testHost, + .CodeActionIndex = index, + .CodeActionEquivalenceKey = equivalenceKey, + .ExactActionSetOffered = actions, + .CodeActionValidationMode = Testing.CodeActionValidationMode.None + } + Await test.RunAsync() End Function #Region "update containing member tests" - + Public Async Function ConvertSingleTupleType(host As TestHost) As Task Dim text = " class Test @@ -38,7 +58,7 @@ end class Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -79,10 +99,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleType_ChangeArgumentNameCase(host As TestHost) As Task Dim text = " @@ -95,7 +115,7 @@ end class Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -136,10 +156,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleTypeNoNames(host As TestHost) As Task Dim text = " class Test @@ -151,7 +171,7 @@ end class Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(1, 2) + dim t1 = New NewStruct(1, 2) end sub end class @@ -192,10 +212,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleTypePartialNames(host As TestHost) As Task Dim text = " class Test @@ -207,7 +227,7 @@ end class Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(1, b:=2) + dim t1 = New NewStruct(1, b:=2) end sub end class @@ -248,10 +268,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertFromType(host As TestHost) As Task Dim text = " class Test @@ -264,7 +284,7 @@ end class Dim expected = " class Test sub Method() - dim t1 as {|Rename:NewStruct|} = New NewStruct(a:=1, b:=2) + dim t1 as NewStruct = New NewStruct(a:=1, b:=2) dim t2 as NewStruct = New NewStruct(a:=1, b:=2) end sub end class @@ -306,10 +326,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertFromType2(host As TestHost) As Task Dim text = " class Test @@ -322,7 +342,7 @@ end class Dim expected = " class Test function Method() as NewStruct - dim t1 as {|Rename:NewStruct|} = New NewStruct(a:=1, b:=2) + dim t1 as NewStruct = New NewStruct(a:=1, b:=2) dim t2 as NewStruct = New NewStruct(a:=1, b:=2) end function end class @@ -364,23 +384,23 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertFromType3(host As TestHost) As Task Dim text = " class Test function Method() as (a as integer, b as integer) dim t1 as [||](a as integer, b as integer) = (a:=1, b:=2) - (b as integer, a as integer) t2 = (b:=1, a:=2) + dim t2 as (b as integer, a as integer) = (b:=1, a:=2) end function end class" Dim expected = " class Test function Method() as NewStruct - dim t1 as {|Rename:NewStruct|} = New NewStruct(a:=1, b:=2) - (b as integer, a as integer) t2 = (b:=1, a:=2) + dim t1 as NewStruct = New NewStruct(a:=1, b:=2) + dim t2 as (b as integer, a as integer) = (b:=1, a:=2) end function end class @@ -421,10 +441,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertFromType4(host As TestHost) As Task Dim text = " class Test @@ -437,7 +457,7 @@ end class" class Test sub Method() dim t1 as NewStruct = New NewStruct(a:=1, b:=2) - dim t2 as {|Rename:NewStruct|} = New NewStruct(a:=1, b:=2) + dim t2 as NewStruct = New NewStruct(a:=1, b:=2) end sub end class @@ -478,10 +498,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleTypeInNamespace(host As TestHost) As Task Dim text = " namespace N @@ -496,7 +516,7 @@ end namespace namespace N class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -538,22 +558,36 @@ namespace N End Structure end namespace " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function TestNonLiteralNames(host As TestHost) As Task Dim text = " class Test sub Method() dim t1 = [||](a:=Goo(), b:=Bar()) end sub + + function goo() as object + end function + + function bar() as object + end function end class" Dim expected = " +Imports System.Collections.Generic + class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(Goo(), Bar()) + dim t1 = New NewStruct(Goo(), Bar()) end sub + + function goo() as object + end function + + function bar() as object + end function end class Friend Structure NewStruct @@ -571,8 +605,8 @@ Friend Structure NewStruct End If Dim other = DirectCast(obj, NewStruct) - Return System.Collections.Generic.EqualityComparer(Of Object).Default.Equals(a, other.a) AndAlso - System.Collections.Generic.EqualityComparer(Of Object).Default.Equals(b, other.b) + Return EqualityComparer(Of Object).Default.Equals(a, other.a) AndAlso + EqualityComparer(Of Object).Default.Equals(b, other.b) End Function Public Overrides Function GetHashCode() As Integer @@ -593,10 +627,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleTypeWithInferredName(host As TestHost) As Task Dim text = " class Test @@ -607,7 +641,7 @@ end class" Dim expected = " class Test sub Method(b as integer) - dim t1 = New {|Rename:NewStruct|}(a:=1, b) + dim t1 = New NewStruct(a:=1, b) end sub end class @@ -648,10 +682,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertMultipleInstancesInSameMethod(host As TestHost) As Task Dim text = " class Test @@ -663,7 +697,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b:=4) end sub end class @@ -705,10 +739,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertMultipleInstancesInSameMethod_DifferingCase(host As TestHost) As Task Dim text = " class Test @@ -720,7 +754,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b:=4) end sub end class @@ -762,10 +796,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertMultipleInstancesAcrossMethods(host As TestHost) As Task Dim text = " class Test @@ -782,7 +816,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b:=4) end sub @@ -829,10 +863,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function OnlyConvertMatchingTypesInSameMethod(host As TestHost) As Task Dim text = " class Test @@ -846,7 +880,7 @@ end class" Dim expected = " class Test sub Method(b as integer) - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b) dim t3 = (a:=4, b:=5, c:=6) dim t4 = (b:=5, a:=6) @@ -890,10 +924,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function TestFixAllMatchesInSingleMethod(host As TestHost) As Task Dim text = " class Test @@ -907,7 +941,7 @@ end class" Dim expected = " class Test sub Method(b as integer) - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b) dim t3 = (a:=4, b:=5, c:=6) dim t4 = (b:=5, a:=6) @@ -951,10 +985,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function TestFixNotAcrossMethods(host As TestHost) As Task Dim text = " class Test @@ -971,7 +1005,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) dim t2 = New NewStruct(a:=3, b:=4) end sub @@ -1018,11 +1052,11 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - - Public Async Function NotIfReferencesAnonymousTypeInternally() As Task + + Public Async Function NotIfReferencesAnonymousTypeInternally(host As TestHost) As Task Dim text = " class Test sub Method() @@ -1030,10 +1064,10 @@ class Test end sub end class" - Await TestMissingInRegularAndScriptAsync(text) + Await TestAsync(text, text, testHost:=host) End Function - + Public Async Function ConvertMultipleNestedInstancesInSameMethod1(host As TestHost) As Task Dim text = " class Test @@ -1046,7 +1080,7 @@ Imports System.Collections.Generic class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, directcast(New NewStruct(a:=1, directcast(nothing, object)), object)) + dim t1 = New NewStruct(a:=1, directcast(New NewStruct(a:=1, directcast(nothing, object)), object)) end sub end class @@ -1087,10 +1121,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertMultipleNestedInstancesInSameMethod2(host As TestHost) As Task Dim text = " class Test @@ -1103,7 +1137,7 @@ Imports System.Collections.Generic class Test sub Method() - dim t1 = New NewStruct(a:=1, directcast(New {|Rename:NewStruct|}(a:=1, directcast(nothing, object)), object)) + dim t1 = New NewStruct(a:=1, directcast(New NewStruct(a:=1, directcast(nothing, object)), object)) end sub end class @@ -1144,10 +1178,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function RenameAnnotationOnStartingPoint(host As TestHost) As Task Dim text = " class Test @@ -1160,7 +1194,7 @@ end class" class Test sub Method() dim t1 = New NewStruct(a:=1, b:=2) - dim t2 = New {|Rename:NewStruct|}(a:=3, b:=4) + dim t2 = New NewStruct(a:=3, b:=4) end sub end class @@ -1201,25 +1235,25 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function CapturedMethodTypeParameters(host As TestHost) As Task Dim text = " imports System.Collections.Generic class Test(of X as {structure}) - sub Method(of Y as {class, new})(x as List(of X), y as Y()) - dim t1 = [||](a:=x, b:=y) + sub Method(of Y as {class, new})(x as List(of X), y1 as Y()) + dim t1 = [||](a:=x, b:=y1) end sub end class" Dim expected = " imports System.Collections.Generic class Test(of X as {structure}) - sub Method(of Y as {class, new})(x as List(of X), y as Y()) - dim t1 = New {|Rename:NewStruct|}(Of X, Y)(x, y) + sub Method(of Y as {class, new})(x as List(of X), y1 as Y()) + dim t1 = New NewStruct(Of X, Y)(x, y1) end sub end class @@ -1261,13 +1295,12 @@ Friend Structure NewStruct(Of X As Structure, Y As {Class, New}) End Structure " - Await TestExactActionSetOfferedAsync(text, { + Await TestAsync(text, expected, testHost:=host, actions:={ FeaturesResources.updating_usages_in_containing_member }) - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) End Function - + Public Async Function NewTypeNameCollision(host As TestHost) As Task Dim text = " class Test @@ -1281,7 +1314,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct1|}(a:=1, b:=2) + dim t1 = New NewStruct1(a:=1, b:=2) end sub end class @@ -1325,10 +1358,10 @@ Friend Structure NewStruct1 End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function NewTypeNameCollision_CaseInsensitive(host As TestHost) As Task Dim text = " class Test @@ -1342,7 +1375,7 @@ end class" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct1|}(a:=1, b:=2) + dim t1 = New NewStruct1(a:=1, b:=2) end sub end class @@ -1386,65 +1419,10 @@ Friend Structure NewStruct1 End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) - End Function - - - Public Async Function TestDuplicatedName(host As TestHost) As Task - Dim text = " -class Test - sub Method() - dim t1 = [||](a:=1, a:=2) - end sub -end class" - Dim expected = " -class Test - sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, a:=2) - end sub -end class - -Friend Structure NewStruct - Public a As Integer - Public a As Integer - - Public Sub New(a As Integer, a As Integer) - Me.a = a - Me.a = a - End Sub - - Public Overrides Function Equals(obj As Object) As Boolean - If Not (TypeOf obj Is NewStruct) Then - Return False - End If - - Dim other = DirectCast(obj, NewStruct) - Return Me.a = other.a AndAlso - Me.a = other.a - End Function - - Public Overrides Function GetHashCode() As Integer - Return (Me.a, Me.a).GetHashCode() - End Function - - Public Sub Deconstruct(ByRef a As Integer, ByRef a As Integer) - a = Me.a - a = Me.a - End Sub - - Public Shared Widening Operator CType(value As NewStruct) As (a As Integer, a As Integer) - Return (value.a, value.a) - End Operator - - Public Shared Widening Operator CType(value As (a As Integer, a As Integer)) As NewStruct - Return New NewStruct(value.a, value.a) - End Operator -End Structure -" - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function TestInLambda1(host As TestHost) As Task Dim text = " imports System @@ -1452,9 +1430,9 @@ imports System class Test sub Method() dim t1 = [||](a:=1, b:=2) - dim a = sub () + dim a = function () dim t2 = (a:=3, b:=4) - end sub() + end function() end sub end class" Dim expected = " @@ -1462,10 +1440,10 @@ imports System class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) - dim a = sub () + dim t1 = New NewStruct(a:=1, b:=2) + dim a = function () dim t2 = New NewStruct(a:=3, b:=4) - end sub() + end function() end sub end class @@ -1506,10 +1484,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function TestInLambda2(host As TestHost) As Task Dim text = " imports System @@ -1517,9 +1495,9 @@ imports System class Test sub Method() dim t1 = (a:=1, b:=2) - dim a = sub () + dim a = function () dim t2 = [||](a:=3, b:=4) - end sub() + end function() end sub end class" Dim expected = " @@ -1528,9 +1506,9 @@ imports System class Test sub Method() dim t1 = New NewStruct(a:=1, b:=2) - dim a = sub () - dim t2 = New {|Rename:NewStruct|}(a:=3, b:=4) - end sub() + dim a = function () + dim t2 = New NewStruct(a:=3, b:=4) + end function() end sub end class @@ -1571,10 +1549,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) + Await TestAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertWithDefaultNames1(host As TestHost) As Task Dim text As String = " class Test @@ -1590,7 +1568,7 @@ end class Dim expected As String = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(1, 2) + dim t1 = New NewStruct(1, 2) dim t2 = New NewStruct(1, 2) dim t3 = (a:=1, b:=2) dim t4 = New NewStruct(item1:=1, item2:=2) @@ -1635,15 +1613,14 @@ Friend Structure NewStruct End Operator End Structure " - Await TestExactActionSetOfferedAsync(text, { + + Await TestAsync(text, expected, testHost:=host, actions:={ FeaturesResources.updating_usages_in_containing_member, FeaturesResources.updating_usages_in_containing_type }) - - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertWithDefaultNames2(host As TestHost) As Task Dim text As String = " class Test @@ -1662,7 +1639,7 @@ class Test dim t1 = New NewStruct(1, 2) dim t2 = New NewStruct(1, 2) dim t3 = (a:=1, b:=2) - dim t4 = New {|Rename:NewStruct|}(item1:=1, item2:=2) + dim t4 = New NewStruct(item1:=1, item2:=2) dim t5 = New NewStruct(item1:=1, item2:=2) end sub end class @@ -1704,42 +1681,31 @@ Friend Structure NewStruct End Operator End Structure " - Await TestExactActionSetOfferedAsync(text, { + Await TestAsync(text, expected, testHost:=host, actions:={ FeaturesResources.updating_usages_in_containing_member, FeaturesResources.updating_usages_in_containing_type }) - - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) End Function - + Public Async Function ConvertSingleTupleTypeWithInaccessibleSystemHashCode(host As TestHost) As Task Dim text = " - - - -Namespace System - Friend Class HashCode - End Class -End Namespace - - - - Assembly1 - class Test sub Method() dim t1 = [||](a:=1, b:=2) end sub -end class - - -" +end class" + + Dim hashCodeText = " +Namespace System + Friend Class HashCode + End Class +End Namespace" Dim expected = " class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -1763,10 +1729,7 @@ Friend Structure NewStruct End Function Public Overrides Function GetHashCode() As Integer - Dim hashCode = 2118541809 - hashCode = hashCode * -1521134295 + a.GetHashCode() - hashCode = hashCode * -1521134295 + b.GetHashCode() - Return hashCode + Return (a, b).GetHashCode() End Function Public Sub Deconstruct(ByRef a As Integer, ByRef b As Integer) @@ -1784,34 +1747,42 @@ Friend Structure NewStruct End Structure " - Await TestInRegularAndScriptAsync(text, expected, testHost:=host) - End Function + Dim test = New VerifyVB.Test With { + .TestHost = host + } + + test.TestState.Sources.Add(text) + test.TestState.AdditionalProjects("Assembly1").Sources.Add(hashCodeText) + test.TestState.AdditionalProjectReferences.Add("Assembly1") + + test.FixedState.Sources.Add(expected) + test.FixedState.AdditionalProjects("Assembly1").Sources.Add(hashCodeText) + test.FixedState.AdditionalProjectReferences.Add("Assembly1") - Protected Overrides Function GetScriptOptions() As ParseOptions - Return Nothing + Await test.RunAsync() End Function #End Region #Region "update containing type tests" - + Public Async Function TestCapturedTypeParameter_UpdateType(host As TestHost) As Task Dim text = " imports System class Test(of T) - sub Method(t as T) - dim t1 = [||](a:=t, b:=2) + sub Method(t2 as T) + dim t1 = [||](a:=t2, b:=2) end sub - dim t as T + dim t3 as T sub Goo() - dim t2 = (a:=t, b:=4) + dim t2 = (a:=t3, b:=4) end sub - sub Blah(of T)(t as T) - dim t2 = (a:=t, b:=4) + sub Blah(of T)(t1 as T) + dim t2 = (a:=t1, b:=4) end sub end class" Dim expected = " @@ -1819,17 +1790,17 @@ imports System Imports System.Collections.Generic class Test(of T) - sub Method(t as T) - dim t1 = New {|Rename:NewStruct|}(Of T)(t, b:=2) + sub Method(t2 as T) + dim t1 = New NewStruct(Of T)(t2, b:=2) end sub - dim t as T + dim t3 as T sub Goo() - dim t2 = New NewStruct(Of T)(t, b:=4) + dim t2 = New NewStruct(Of T)(t3, b:=4) end sub - sub Blah(of T)(t as T) - dim t2 = (a:=t, b:=4) + sub Blah(of T)(t1 as T) + dim t2 = (a:=t1, b:=4) end sub end class @@ -1871,14 +1842,13 @@ Friend Structure NewStruct(Of T) End Structure " - Await TestExactActionSetOfferedAsync(text, { + Await TestAsync(text, expected, index:=1, equivalenceKey:=Scope.ContainingType.ToString(), testHost:=host, actions:={ FeaturesResources.updating_usages_in_containing_member, FeaturesResources.updating_usages_in_containing_type }) - Await TestInRegularAndScriptAsync(text, expected, index:=1, testHost:=host) End Function - + Public Async Function UpdateAllInType_SinglePart_SingleFile(host As TestHost) As Task Dim text = " imports System @@ -1902,7 +1872,7 @@ imports System class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub sub Goo() @@ -1952,10 +1922,10 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, index:=1, testHost:=host) + Await TestAsync(text, expected, index:=1, equivalenceKey:=Scope.ContainingType.ToString(), testHost:=host) End Function - + Public Async Function UpdateAllInType_MultiplePart_SingleFile(host As TestHost) As Task Dim text = " imports System @@ -1980,7 +1950,7 @@ imports System partial class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class partial class Test @@ -2031,15 +2001,13 @@ Friend Structure NewStruct End Operator End Structure " - Await TestInRegularAndScriptAsync(text, expected, index:=1, testHost:=host) + + Await TestAsync(text, expected, index:=1, equivalenceKey:=Scope.ContainingType.ToString(), testHost:=host) End Function - + Public Async Function UpdateAllInType_MultiplePart_MultipleFile(host As TestHost) As Task - Dim text = " - - - + Dim text1 = " imports System partial class Test @@ -2052,9 +2020,8 @@ partial class Other sub Method() dim t1 = (a:=1, b:=2) end sub -end class - - +end class" + Dim text2 = " imports System partial class Test @@ -2067,20 +2034,14 @@ partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" +end class" - Dim expected = " - - - + Dim expected1 = " imports System partial class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -2110,10 +2071,7 @@ Friend Structure NewStruct End Function Public Overrides Function GetHashCode() As Integer - Dim hashCode = 2118541809 - hashCode = hashCode * -1521134295 + a.GetHashCode() - hashCode = hashCode * -1521134295 + b.GetHashCode() - Return hashCode + Return (a, b).GetHashCode() End Function Public Sub Deconstruct(ByRef a As Integer, ByRef b As Integer) @@ -2129,8 +2087,9 @@ Friend Structure NewStruct Return New NewStruct(value.a, value.b) End Operator End Structure - - +" + + Dim expected2 = " imports System partial class Test @@ -2143,23 +2102,30 @@ partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" - Await TestInRegularAndScriptAsync(text, expected, index:=1, testHost:=host) +end class" + + Dim test = New VerifyVB.Test With { + .CodeActionIndex = 1, + .CodeActionEquivalenceKey = Scope.ContainingType.ToString(), + .TestHost = host + } + + test.TestState.Sources.Add(text1) + test.TestState.Sources.Add(text2) + + test.FixedState.Sources.Add(expected1) + test.FixedState.Sources.Add(expected2) + + Await test.RunAsync() End Function #End Region #Region "update containing project tests" - + Public Async Function UpdateAllInProject_MultiplePart_MultipleFile_WithNamespace(host As TestHost) As Task - Dim text = " - - - + Dim text1 = " imports System namespace N @@ -2174,9 +2140,9 @@ namespace N dim t1 = (a:=1, b:=2) end sub end class -end namespace - - +end namespace" + + Dim text2 = " imports System partial class Test @@ -2189,21 +2155,15 @@ partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" +end class" - Dim expected = " - - - + Dim expected1 = " imports System namespace N partial class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -2233,10 +2193,7 @@ namespace N End Function Public Overrides Function GetHashCode() As Integer - Dim hashCode = 2118541809 - hashCode = hashCode * -1521134295 + a.GetHashCode() - hashCode = hashCode * -1521134295 + b.GetHashCode() - Return hashCode + Return (a, b).GetHashCode() End Function Public Sub Deconstruct(ByRef a As Integer, ByRef b As Integer) @@ -2252,9 +2209,9 @@ namespace N Return New NewStruct(value.a, value.b) End Operator End Structure -end namespace - - +end namespace" + + Dim expected2 = " imports System partial class Test @@ -2267,23 +2224,30 @@ partial class Other sub Goo() dim t1 = New N.NewStruct(a:=1, b:=2) end sub -end class - - -" - Await TestInRegularAndScriptAsync(text, expected, index:=2, testHost:=host) +end class" + + Dim test = New VerifyVB.Test with { + .CodeActionIndex = 2, + .CodeActionEquivalenceKey = Scope.ContainingProject.ToString(), + .TestHost = host + } + + test.TestState.Sources.Add(text1) + test.TestState.Sources.Add(text2) + + test.FixedState.Sources.Add(expected1) + test.FixedState.Sources.Add(expected2) + + Await test.RunAsync() End Function #End Region #Region "update dependent projects" - + Public Async Function UpdateDependentProjects_DirectDependency(host As TestHost) As Task - Dim text = " - - - + Dim text1 = " imports System partial class Test @@ -2296,31 +2260,21 @@ partial class Other sub Method() dim t1 = (a:=1, b:=2) end sub -end class - - - - Assembly1 - +end class" + Dim text2 = " imports System partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" - Dim expected = " - - - +end class" + Dim expected1 = " imports System partial class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -2350,10 +2304,7 @@ Public Structure NewStruct End Function Public Overrides Function GetHashCode() As Integer - Dim hashCode = 2118541809 - hashCode = hashCode * -1521134295 + a.GetHashCode() - hashCode = hashCode * -1521134295 + b.GetHashCode() - Return hashCode + Return (a, b).GetHashCode() End Function Public Sub Deconstruct(ByRef a As Integer, ByRef b As Integer) @@ -2369,30 +2320,37 @@ Public Structure NewStruct Return New NewStruct(value.a, value.b) End Operator End Structure - - - - Assembly1 - +" + + Dim expected2 = " imports System partial class Other sub Goo() dim t1 = New NewStruct(a:=1, b:=2) end sub -end class - - -" - Await TestInRegularAndScriptAsync(text, expected, index:=3, testHost:=host) +end class" + + Dim test = New VerifyVB.Test With { + .CodeActionIndex = 3, + .CodeActionEquivalenceKey = Scope.DependentProjects.ToString(), + .TestHost = host + } + + test.TestState.Sources.Add(text1) + test.TestState.AdditionalProjects("DependencyProject").Sources.Add(text2) + test.TestState.AdditionalProjects("DependencyProject").AdditionalProjectReferences.Add("TestProject") + + test.FixedState.Sources.Add(expected1) + test.FixedState.AdditionalProjects("DependencyProject").Sources.Add(expected2) + test.FixedState.AdditionalProjects("DependencyProject").AdditionalProjectReferences.Add("TestProject") + + Await test.RunAsync() End Function - + Public Async Function UpdateDependentProjects_NoDependency(host As TestHost) As Task - Dim text = " - - - + Dim text1 = " imports System partial class Test @@ -2405,30 +2363,21 @@ partial class Other sub Method() dim t1 = (a:=1, b:=2) end sub -end class - - - - +end class" + Dim text2 = " imports System partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" - Dim expected = " - - - +end class" + Dim expected1 = " imports System partial class Test sub Method() - dim t1 = New {|Rename:NewStruct|}(a:=1, b:=2) + dim t1 = New NewStruct(a:=1, b:=2) end sub end class @@ -2458,10 +2407,7 @@ Public Structure NewStruct End Function Public Overrides Function GetHashCode() As Integer - Dim hashCode = 2118541809 - hashCode = hashCode * -1521134295 + a.GetHashCode() - hashCode = hashCode * -1521134295 + b.GetHashCode() - Return hashCode + Return (a, b).GetHashCode() End Function Public Sub Deconstruct(ByRef a As Integer, ByRef b As Integer) @@ -2477,21 +2423,30 @@ Public Structure NewStruct Return New NewStruct(value.a, value.b) End Operator End Structure - - - - +" + + Dim expected2 = " imports System partial class Other sub Goo() dim t1 = (a:=1, b:=2) end sub -end class - - -" - Await TestInRegularAndScriptAsync(text, expected, index:=3, testHost:=host) +end class" + + Dim test = New VerifyVB.Test With { + .CodeActionIndex = 3, + .CodeActionEquivalenceKey = Scope.DependentProjects.ToString(), + .TestHost = host + } + + test.TestState.Sources.Add(text1) + test.TestState.AdditionalProjects("DependencyProject").Sources.Add(text2) + + test.FixedState.Sources.Add(expected1) + test.FixedState.AdditionalProjects("DependencyProject").Sources.Add(expected2) + + Await test.RunAsync() End Function #End Region diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb index 44c0cc0701436..775c3b9c97e59 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb @@ -7,6 +7,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod Imports Microsoft.CodeAnalysis.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.GenerateMethod + Partial Public Class GenerateMethodTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest @@ -14,7 +15,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Genera Return (Nothing, New GenerateParameterizedMemberCodeFixProvider()) End Function - + Public Async Function TestSimpleInvocationIntoSameType() As Task Await TestInRegularAndScriptAsync( "Class C @@ -35,7 +36,7 @@ Class C End Class") End Function - + Public Async Function TestNotForExpressionOnLeftOfAssign() As Task Await TestMissingAsync( "Class C @@ -45,7 +46,7 @@ End Class") End Class") End Function - + Public Async Function TestNameMatchesNamespaceName() As Task Await TestInRegularAndScriptAsync( @@ -71,7 +72,7 @@ Namespace N End Namespace") End Function - + Public Async Function TestSimpleInvocationOffOfMe() As Task Await TestInRegularAndScriptAsync( "Class C @@ -92,7 +93,7 @@ Class C End Class") End Function - + Public Async Function TestSimpleInvocationOffOfType() As Task Await TestInRegularAndScriptAsync( "Class C @@ -113,7 +114,7 @@ Class C End Class") End Function - + Public Async Function TestSimpleInvocationValueExpressionArg() As Task Await TestInRegularAndScriptAsync( "Class C @@ -134,7 +135,7 @@ Class C End Class") End Function - + Public Async Function TestSimpleInvocationMultipleValueExpressionArg() As Task Await TestInRegularAndScriptAsync( "Class C @@ -155,7 +156,7 @@ Class C End Class") End Function - + Public Async Function TestSimpleInvocationValueArg() As Task Await TestInRegularAndScriptAsync( "Class C @@ -176,7 +177,7 @@ Class C End Class") End Function - + Public Async Function TestSimpleInvocationNamedValueArg() As Task Await TestInRegularAndScriptAsync( "Class C @@ -197,7 +198,7 @@ Class C End Class") End Function - + Public Async Function TestGenerateAfterMethod() As Task Await TestInRegularAndScriptAsync( "Class C @@ -223,7 +224,7 @@ Class C End Class") End Function - + Public Async Function TestInterfaceNaming() As Task Await TestInRegularAndScriptAsync( "Class C @@ -249,7 +250,7 @@ Class C End Class") End Function - + Public Async Function TestFuncArg0() As Task Await TestInRegularAndScriptAsync( "Class C @@ -275,7 +276,7 @@ Class C End Class") End Function - + Public Async Function TestFuncArg1() As Task Await TestInRegularAndScriptAsync( "Class C @@ -301,7 +302,7 @@ Class C End Class") End Function - + Public Async Function TestAddressOf1() As Task Await TestInRegularAndScriptAsync( "Class C @@ -317,15 +318,17 @@ Class C Sub M(i As Integer) Goo(AddressOf NextMethod) End Sub - Private Sub Goo(nextMethod As Global.System.Func(Of Integer, String)) + + Private Sub Goo(value As Func(Of Integer, String)) Throw New NotImplementedException() End Sub + Function NextMethod(i As Integer) As String End Function End Class") End Function - + Public Async Function TestActionArg() As Task Await TestInRegularAndScriptAsync( "Class C @@ -348,7 +351,7 @@ Private Sub Goo(nextMethod As Object) End Class") End Function - + Public Async Function TestActionArg1() As Task Await TestInRegularAndScriptAsync( "Class C @@ -374,7 +377,7 @@ Class C End Class") End Function - + Public Async Function TestTypeInference() As Task Await TestInRegularAndScriptAsync( "Class C @@ -397,7 +400,7 @@ Class C End Class") End Function - + Public Async Function TestMemberAccessArgumentName() As Task Await TestInRegularAndScriptAsync( "Class C @@ -421,7 +424,7 @@ Class C End Class") End Function - + Public Async Function TestParenthesizedArgumentName() As Task Await TestInRegularAndScriptAsync( "Class C @@ -445,7 +448,7 @@ Class C End Class") End Function - + Public Async Function TestCastedArgumentName() As Task Await TestInRegularAndScriptAsync( "Class C @@ -470,7 +473,7 @@ Class Bar End Class") End Function - + Public Async Function TestDuplicateNames() As Task Await TestInRegularAndScriptAsync( "Class C @@ -498,7 +501,7 @@ Class Bar End Class") End Function - + Public Async Function TestGenericArgs1() As Task Await TestInRegularAndScriptAsync( "Class C @@ -519,7 +522,7 @@ Class C End Class") End Function - + Public Async Function TestGenericArgs2() As Task Await TestInRegularAndScriptAsync( "Class C @@ -540,7 +543,7 @@ Class C End Class") End Function - + Public Async Function TestGenericArgsFromMethod() As Task Await TestInRegularAndScriptAsync( @@ -562,7 +565,7 @@ Class C End Class") End Function - + Public Async Function TestGenericArgThatIsTypeParameter() As Task Await TestInRegularAndScriptAsync( "Class C @@ -583,7 +586,7 @@ Class C End Class") End Function - + Public Async Function TestMultipleGenericArgsThatAreTypeParameters() As Task Await TestInRegularAndScriptAsync( "Class C @@ -604,7 +607,7 @@ Class C End Class") End Function - + Public Async Function TestMultipleGenericArgsFromMethod() As Task Await TestInRegularAndScriptAsync( @@ -626,7 +629,7 @@ Class C End Class") End Function - + Public Async Function TestMultipleGenericArgsFromMethod2() As Task Await TestInRegularAndScriptAsync( @@ -648,7 +651,7 @@ Class C End Class") End Function - + Public Async Function TestGenerateIntoOuterThroughInstance() As Task Await TestInRegularAndScriptAsync( "Class Outer @@ -673,7 +676,7 @@ Class Outer End Class") End Function - + Public Async Function TestGenerateIntoOuterThroughClass() As Task Await TestInRegularAndScriptAsync( "Class Outer @@ -698,7 +701,7 @@ Class Outer End Class") End Function - + Public Async Function TestGenerateIntoSiblingThroughInstance() As Task Await TestInRegularAndScriptAsync( "Class C @@ -722,7 +725,7 @@ Class Sibling End Class") End Function - + Public Async Function TestGenerateIntoSiblingThroughClass() As Task Await TestInRegularAndScriptAsync( "Class C @@ -746,7 +749,7 @@ Class Sibling End Class") End Function - + Public Async Function TestGenerateIntoInterfaceThroughInstance() As Task Await TestInRegularAndScriptAsync( "Class C @@ -766,7 +769,7 @@ Interface ISibling End Interface") End Function - + Public Async Function TestGenerateAbstractIntoSameType() As Task Await TestInRegularAndScriptAsync( @@ -785,7 +788,7 @@ End Class", index:=1) End Function - + Public Async Function TestGenerateIntoModule() As Task Await TestInRegularAndScriptAsync( @@ -807,7 +810,7 @@ Module Class C End Module") End Function - + Public Async Function TestInference1() As Task Await TestInRegularAndScriptAsync( @@ -831,7 +834,7 @@ Class C End Class") End Function - + Public Async Function TestEscaping1() As Task Await TestInRegularAndScriptAsync( @@ -853,7 +856,7 @@ Class C End Class") End Function - + Public Async Function TestExplicitCall() As Task Await TestInRegularAndScriptAsync( @@ -875,7 +878,7 @@ Class C End Class") End Function - + Public Async Function TestImplicitCall() As Task Await TestInRegularAndScriptAsync( @@ -897,7 +900,7 @@ Class C End Class") End Function - + Public Async Function TestArrayAccess1() As Task Await TestMissingInRegularAndScriptAsync("Class C @@ -907,7 +910,7 @@ End Class") End Class") End Function - + Public Async Function TestTypeCharacterInteger() As Task Await TestInRegularAndScriptAsync( @@ -929,7 +932,7 @@ Class C End Class") End Function - + Public Async Function TestTypeCharacterLong() As Task Await TestInRegularAndScriptAsync( @@ -951,7 +954,7 @@ Class C End Class") End Function - + Public Async Function TestTypeCharacterDecimal() As Task Await TestInRegularAndScriptAsync( @@ -973,7 +976,7 @@ Class C End Class") End Function - + Public Async Function TestTypeCharacterSingle() As Task Await TestInRegularAndScriptAsync( @@ -995,7 +998,7 @@ Class C End Class") End Function - + Public Async Function TestTypeCharacterDouble() As Task Await TestInRegularAndScriptAsync( @@ -1017,7 +1020,7 @@ Class C End Class") End Function - + Public Async Function TestTypeCharacterString() As Task Await TestInRegularAndScriptAsync( @@ -1039,7 +1042,7 @@ Class C End Class") End Function - + Public Async Function TestNewLines() As Task Await TestInRegularAndScriptAsync( @@ -1061,7 +1064,7 @@ Public Class C End Class.Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestNewLines2() As Task Await TestInRegularAndScriptAsync( @@ -1088,7 +1091,7 @@ Public Class D End Class.Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestArgumentTypeVoid() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1111,7 +1114,7 @@ Module Program End Module") End Function - + Public Async Function TestGenerateFromImplementsClause() As Task Await TestInRegularAndScriptAsync( "Class Program @@ -1131,7 +1134,7 @@ Interface IGoo End Interface") End Function - + Public Async Function TestInScript1() As Task Await TestAsync( @@ -1151,7 +1154,7 @@ End Sub parseOptions:=GetScriptOptions()) End Function - + Public Async Function TestInTopLevelImplicitClass1() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1169,7 +1172,7 @@ End Sub ") End Function - + Public Async Function TestInNamespaceImplicitClass1() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1190,7 +1193,7 @@ Namespace N End Namespace") End Function - + Public Async Function TestInNamespaceImplicitClass_FieldInitializer() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1207,7 +1210,7 @@ Namespace N End Namespace") End Function - + Public Async Function TestClashesWithMethod1() As Task Await TestMissingInRegularAndScriptAsync( "Class Program @@ -1220,7 +1223,7 @@ Interface IGoo End Interface") End Function - + Public Async Function TestClashesWithMethod2() As Task Await TestMissingInRegularAndScriptAsync( "Class Program @@ -1233,7 +1236,7 @@ Interface IGoo End Interface") End Function - + Public Async Function TestClashesWithMethod3() As Task Await TestInRegularAndScriptAsync( "Class C @@ -1255,7 +1258,7 @@ Interface IGoo End Interface") End Function - + Public Async Function TestClashesWithMethod4() As Task Await TestInRegularAndScriptAsync( "Class C @@ -1277,7 +1280,7 @@ Interface IGoo End Interface") End Function - + Public Async Function TestClashesWithMethod5() As Task Await TestInRegularAndScriptAsync( "Class C @@ -1299,7 +1302,7 @@ Friend Interface IGoo End Interface") End Function - + Public Async Function TestClashesWithMethod6() As Task Await TestInRegularAndScriptAsync( "Class C @@ -1321,7 +1324,7 @@ Friend Interface IGoo End Interface") End Function - + Public Async Function TestNoStaticGenerationIntoInterface() As Task Await TestMissingInRegularAndScriptAsync( @@ -1334,7 +1337,7 @@ Class Program End Class") End Function - + Public Async Function TestEscapeParameterName() As Task Await TestInRegularAndScriptAsync( @@ -1356,7 +1359,7 @@ End Module", End Module") End Function - + Public Async Function TestDoNotUseUnavailableTypeParameter() As Task Await TestInRegularAndScriptAsync( @@ -1378,7 +1381,7 @@ Class Test End Class") End Function - + Public Async Function TestDoNotUseTypeParametersFromContainingType() As Task Await TestInRegularAndScriptAsync( @@ -1400,7 +1403,7 @@ Class Test(Of T) End Class") End Function - + Public Async Function TestNameSimplification1() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1421,7 +1424,7 @@ Class C End Class") End Function - + Public Async Function TestFormattingOfMembers() As Task Await TestInRegularAndScriptAsync( @@ -1453,7 +1456,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestInAddressOfExpression1() As Task Await TestInRegularAndScriptAsync( @@ -1477,7 +1480,7 @@ Class C End Class") End Function - + Public Async Function TestNotOfferedForInferredGenericMethodArgs() As Task Await TestMissingInRegularAndScriptAsync( @@ -1490,7 +1493,7 @@ End Class") End Class") End Function - + Public Async Function TestDelegateInAsClause() As Task Await TestInRegularAndScriptAsync( @@ -1514,7 +1517,7 @@ Class C End Class") End Function - + Public Async Function TestMissingOnImplementedInterfaceMethod() As Task Await TestMissingInRegularAndScriptAsync( @@ -1528,7 +1531,7 @@ Friend Interface ITest End Interface") End Function - + Public Async Function TestNotOnConstructorInitializer() As Task Await TestMissingInRegularAndScriptAsync( @@ -1539,7 +1542,7 @@ End Interface") End Class") End Function - + Public Async Function TestMultipleImportsAdded() As Task Await TestInRegularAndScriptAsync( @@ -1562,7 +1565,7 @@ Module Program End Module") End Function - + Public Async Function TestCompilationMemberImports() As Task Await TestAsync( @@ -1584,7 +1587,7 @@ parseOptions:=Nothing, compilationOptions:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithGlobalImports(GlobalImport.Parse("System"), GlobalImport.Parse("System.Collections.Generic"))) End Function - + Public Async Function TestForEachWithNoControlVariableType() As Task Await TestAsync( @@ -1606,7 +1609,7 @@ parseOptions:=Nothing, compilationOptions:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithGlobalImports(GlobalImport.Parse("System"), GlobalImport.Parse("System.Collections.Generic"))) End Function - + Public Async Function TestElseIfStatement() As Task Await TestAsync( @@ -1632,7 +1635,7 @@ parseOptions:=Nothing, compilationOptions:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithGlobalImports(GlobalImport.Parse("System"))) End Function - + Public Async Function TestForStatement() As Task Await TestAsync( @@ -1654,7 +1657,7 @@ parseOptions:=Nothing, compilationOptions:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithGlobalImports(GlobalImport.Parse("System"))) End Function - + Public Async Function TestArrayOfAnonymousTypes() As Task Await TestInRegularAndScriptAsync( @@ -1690,7 +1693,7 @@ Module Program End Module") End Function - + Public Async Function TestMissingOnHiddenType() As Task Await TestMissingInRegularAndScriptAsync( @@ -1707,7 +1710,7 @@ EndClass .Value) End Function - + Public Async Function TestDoNotGenerateIntoHiddenRegion1_NoImports() As Task Await TestInRegularAndScriptAsync( @@ -1734,7 +1737,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestDoNotGenerateIntoHiddenRegion1_WithImports() As Task Await TestInRegularAndScriptAsync( @@ -1770,7 +1773,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestDoNotGenerateIntoHiddenRegion2() As Task Await TestInRegularAndScriptAsync( @@ -1807,7 +1810,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestDoNotGenerateIntoHiddenRegion3() As Task Await TestInRegularAndScriptAsync( @@ -1850,7 +1853,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestAddressOfInference1() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -1873,7 +1876,7 @@ Module Program End Module") End Function - + Public Async Function TestClassStatementTerminators1() As Task Await TestInRegularAndScriptAsync( @@ -1897,7 +1900,7 @@ Class B End Class") End Function - + Public Async Function TestOmittedArguments1() As Task Await TestInRegularAndScriptAsync( @@ -1919,7 +1922,7 @@ Module Program End Module") End Function - + Public Async Function TestOmittedArguments2() As Task Await TestInRegularAndScriptAsync( @@ -1941,7 +1944,7 @@ Module Program End Module") End Function - + Public Async Function TestOmittedArguments3() As Task Await TestInRegularAndScriptAsync( @@ -1963,7 +1966,7 @@ Module Program End Module") End Function - + Public Async Function TestOmittedArguments4() As Task Await TestInRegularAndScriptAsync( @@ -1985,7 +1988,7 @@ Module Program End Module") End Function - + Public Async Function TestOmittedArguments5() As Task Await TestInRegularAndScriptAsync( @@ -2007,7 +2010,7 @@ Module Program End Module") End Function - + Public Async Function TestOmittedArguments6() As Task Await TestInRegularAndScriptAsync( @@ -2029,7 +2032,7 @@ Module Program End Module") End Function - + Public Async Function TestNotOnMissingMethodName() As Task Await TestMissingInRegularAndScriptAsync("Class C @@ -2039,7 +2042,7 @@ End Module") End Class") End Function - + Public Async Function TestGenerateFromEventHandler() As Task Await TestInRegularAndScriptAsync( @@ -2070,7 +2073,7 @@ Module Module1 End Module") End Function - + Public Async Function TestCapturedMethodTypeParameterThroughLambda() As Task Await TestInRegularAndScriptAsync( @@ -2094,7 +2097,7 @@ Module M End Module") End Function - + Public Async Function TestTypeParameterAndParameterConflict1() As Task Await TestInRegularAndScriptAsync( "Imports System @@ -2119,7 +2122,7 @@ Module M End Module") End Function - + Public Async Function TestTypeParameterAndParameterConflict2() As Task Await TestInRegularAndScriptAsync( @@ -2145,7 +2148,7 @@ Module M End Module") End Function - + Public Async Function TestCollectionInitializer1() As Task Await TestInRegularAndScriptAsync( @@ -2167,7 +2170,7 @@ Module Program End Module") End Function - + Public Async Function TestCollectionInitializer2() As Task Await TestInRegularAndScriptAsync( @@ -2189,7 +2192,7 @@ Module M End Module") End Function - + Public Async Function TestParameterizedProperty1() As Task Await TestInRegularAndScriptAsync( @@ -2211,7 +2214,7 @@ Module Program End Module") End Function - + Public Async Function TestParameterizedProperty2() As Task Await TestInRegularAndScriptAsync( @@ -2239,7 +2242,7 @@ End Module", index:=1) End Function - + Public Async Function TestGenerateMethodWithLambda_1() As Task Await TestInRegularAndScriptAsync( @@ -2277,7 +2280,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodWithLambda_2() As Task Await TestInRegularAndScriptAsync( @@ -2315,7 +2318,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodWithLambda_3() As Task Await TestInRegularAndScriptAsync( @@ -2353,7 +2356,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodForDifferentParameterName() As Task Await TestInRegularAndScriptAsync( @@ -2387,7 +2390,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodForSameNamedButGenericUsage_1() As Task Await TestInRegularAndScriptAsync( @@ -2421,7 +2424,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodForSameNamedButGenericUsage_2() As Task Await TestInRegularAndScriptAsync( @@ -2463,7 +2466,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodForAwaitWithoutParenthesis() As Task Await TestInRegularAndScriptAsync( @@ -2488,7 +2491,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodTooManyArgs1() As Task Await TestInRegularAndScriptAsync( @@ -2517,7 +2520,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNamespaceNotExpression1() As Task Await TestInRegularAndScriptAsync( @@ -2541,7 +2544,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoArgumentCountOverloadCandidates1() As Task Await TestInRegularAndScriptAsync( @@ -2593,7 +2596,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodFunctionResultCannotBeIndexed1() As Task Await TestInRegularAndScriptAsync( @@ -2620,7 +2623,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoCallableOverloadCandidates2() As Task Await TestInRegularAndScriptAsync( @@ -2652,7 +2655,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoNonNarrowingOverloadCandidates2() As Task Await TestInRegularAndScriptAsync( @@ -2744,7 +2747,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoNonNarrowingOverloadCandidates3() As Task Await TestInRegularAndScriptAsync( @@ -2814,7 +2817,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoNonNarrowingOverloadCandidates4() As Task Await TestInRegularAndScriptAsync( @@ -2886,7 +2889,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodArgumentNarrowing() As Task Await TestInRegularAndScriptAsync( @@ -2954,7 +2957,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodArgumentNarrowing2() As Task Await TestInRegularAndScriptAsync( @@ -3022,7 +3025,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodArgumentNarrowing3() As Task Await TestInRegularAndScriptAsync( @@ -3090,7 +3093,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodNoMostSpecificOverload2() As Task Await TestInRegularAndScriptAsync( @@ -3150,7 +3153,7 @@ End Module .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodInsideNameOf() As Task Await TestInRegularAndScriptAsync( @@ -3178,7 +3181,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodInsideNameOf2() As Task Await TestInRegularAndScriptAsync( @@ -3216,7 +3219,7 @@ End Namespace .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodWithNameOfArgument() As Task Await TestInRegularAndScriptAsync( @@ -3241,7 +3244,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodWithLambdaAndNameOfArgument() As Task Await TestInRegularAndScriptAsync( @@ -3266,7 +3269,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis() As Task Await TestInRegularAndScriptAsync( @@ -3288,7 +3291,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis2() As Task Await TestInRegularAndScriptAsync( @@ -3310,7 +3313,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis3() As Task Await TestInRegularAndScriptAsync( @@ -3332,7 +3335,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis4() As Task Await TestInRegularAndScriptAsync( @@ -3354,7 +3357,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis5() As Task Await TestInRegularAndScriptAsync( @@ -3387,7 +3390,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis6() As Task Await TestInRegularAndScriptAsync( @@ -3418,7 +3421,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis7() As Task Await TestInRegularAndScriptAsync( @@ -3449,7 +3452,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis8() As Task Await TestInRegularAndScriptAsync( @@ -3480,7 +3483,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis9() As Task Await TestInRegularAndScriptAsync( @@ -3511,7 +3514,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis10() As Task Await TestInRegularAndScriptAsync( @@ -3542,7 +3545,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccessNoParenthesis11() As Task Await TestInRegularAndScriptAsync( @@ -3573,7 +3576,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccess() As Task Await TestInRegularAndScriptAsync( @@ -3595,7 +3598,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccess2() As Task Await TestInRegularAndScriptAsync( @@ -3617,7 +3620,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccess3() As Task Await TestInRegularAndScriptAsync( @@ -3639,7 +3642,7 @@ Public Class C End Class") End Function - + Public Async Function TestGenerateMethodConditionalAccess4() As Task Await TestInRegularAndScriptAsync( @@ -3761,7 +3764,7 @@ End Class", index:=1) End Function - + Public Async Function TestGenerateMethodConditionalAccess5() As Task @@ -3784,7 +3787,7 @@ Public Structure C End Structure") End Function - + Public Async Function TestGenerateMethodConditionalInPropertyInitializer() As Task Await TestInRegularAndScriptAsync( "Module Program @@ -3801,7 +3804,7 @@ Module Program End Module") End Function - + Public Async Function TestGenerateMethodConditionalInPropertyInitializer2() As Task Await TestInRegularAndScriptAsync( "Module Program @@ -3818,7 +3821,7 @@ Module Program End Module") End Function - + Public Async Function TestGenerateMethodTypeOf() As Task Await TestInRegularAndScriptAsync( "Module C @@ -3841,7 +3844,7 @@ Module C End Module") End Function - + Public Async Function TestGenerateMethodTypeOf2() As Task Await TestInRegularAndScriptAsync( "Module C @@ -3864,7 +3867,7 @@ Module C End Module") End Function - + Public Async Function TestGenerateMethodConfigureAwaitFalse() As Task Await TestInRegularAndScriptAsync( @@ -3923,7 +3926,7 @@ End Module", index:=1) End Function - + Public Async Function TestGenerateMethodWithMethodChaining() As Task Await TestInRegularAndScriptAsync( @@ -3949,7 +3952,7 @@ Module M End Module") End Function - + Public Async Function TestGenerateMethodInTypeOfIsNot() As Task Await TestInRegularAndScriptAsync( @@ -3977,7 +3980,7 @@ Module Program End Module") End Function - + Public Async Function TestInCollectionInitializers1() As Task Await TestInRegularAndScriptAsync( @@ -4001,7 +4004,7 @@ Module Program End Module") End Function - + Public Async Function TestInCollectionInitializers2() As Task Await TestInRegularAndScriptAsync( @@ -4025,7 +4028,7 @@ Module Program End Module") End Function - + Public Async Function TestGenerateMethodWithMultipleOfSameGenericType() As Task Await TestInRegularAndScriptAsync( @@ -4057,7 +4060,7 @@ End Namespace .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function TestGenerateMethodOffOfExistingProperty() As Task Await TestInRegularAndScriptAsync( @@ -4104,7 +4107,7 @@ End Class .Value.Replace(vbLf, vbCrLf)) End Function - + Public Async Function MethodWithTuple() As Task Await TestInRegularAndScriptAsync( "Class Program @@ -4125,7 +4128,7 @@ Class Program End Class") End Function - + Public Async Function TupleElement1() As Task Await TestInRegularAndScriptAsync( @@ -4153,7 +4156,7 @@ End Class ") End Function - + Public Async Function TupleElement2() As Task Await TestInRegularAndScriptAsync( @@ -4181,7 +4184,7 @@ End Class ") End Function - + Public Async Function MethodWithTupleWithNames() As Task Await TestInRegularAndScriptAsync( "Class Program @@ -4202,7 +4205,7 @@ Class Program End Class") End Function - + Public Async Function MethodWithTupleWithOneName() As Task Await TestInRegularAndScriptAsync( "Class Program @@ -4223,7 +4226,7 @@ Class Program End Class") End Function - + Public Async Function TestWithSameMethodNameAsTypeName1() As Task Await TestInRegularAndScriptAsync( @@ -4253,7 +4256,7 @@ Enum Goo End Enum") End Function - + Public Async Function TestWithSameMethodNameAsTypeName2() As Task Await TestInRegularAndScriptAsync( @@ -4279,7 +4282,7 @@ End Class Delegate Sub Goo()") End Function - + Public Async Function TestWithSameMethodNameAsTypeName3() As Task Await TestInRegularAndScriptAsync( @@ -4310,7 +4313,7 @@ Class Goo End Class") End Function - + Public Async Function TestWithSameMethodNameAsTypeName4() As Task Await TestInRegularAndScriptAsync( @@ -4340,7 +4343,7 @@ Structure Goo End Structure") End Function - + Public Async Function TestWithSameMethodNameAsTypeName5() As Task Await TestInRegularAndScriptAsync( @@ -4370,7 +4373,7 @@ Interface Goo End Interface") End Function - + Public Async Function TestWithSameMethodNameAsTypeName6() As Task Await TestInRegularAndScriptAsync( @@ -4400,7 +4403,7 @@ Namespace Goo End Namespace") End Function - + Public Async Function TestAcrossFiles() As Task Await TestInRegularAndScriptAsync( @@ -4465,6 +4468,151 @@ Public Class DataContainer Return Nothing End Function +End Class") + End Function + + + Public Async Function TestMethodReference1() As Task + Await TestInRegularAndScriptAsync( +"Class C + Function OtherMethod() As Integer + return 0 + End Function + + Sub M() + [|Goo|](OtherMethod) + End Sub +End Class", +"Imports System + +Class C + Function OtherMethod() As Integer + return 0 + End Function + + Sub M() + Goo(OtherMethod) + End Sub + + Private Sub Goo(otherMethod As Integer) + Throw New NotImplementedException() + End Sub +End Class") + End Function + + + Public Async Function TestMethodReference2() As Task + Await TestInRegularAndScriptAsync( +"Class C + Function OtherMethod() As Integer + return 0 + End Function + + Sub M() + [|Goo|](AddressOf OtherMethod) + End Sub +End Class", +"Imports System + +Class C + Function OtherMethod() As Integer + return 0 + End Function + + Sub M() + Goo(AddressOf OtherMethod) + End Sub + + Private Sub Goo(value As Func(Of Integer)) + Throw New NotImplementedException() + End Sub +End Class") + End Function + + + Public Async Function TestMethodReference3() As Task + Await TestInRegularAndScriptAsync( +"Class C + Function OtherMethod(x as Integer) As Integer + return 0 + End Function + + Sub M() + [|Goo|](OtherMethod) + End Sub +End Class", +"Imports System + +Class C + Function OtherMethod(x as Integer) As Integer + return 0 + End Function + + Sub M() + Goo(OtherMethod) + End Sub + + Private Sub Goo(otherMethod As Func(Of Integer, Integer)) + Throw New NotImplementedException() + End Sub +End Class") + End Function + + + Public Async Function TestMethodReference4() As Task + Await TestInRegularAndScriptAsync( +"Class C + Function OtherMethod(optional x as Integer = 0) As Integer + return 0 + End Function + + Sub M() + [|Goo|](OtherMethod) + End Sub +End Class", +"Imports System + +Class C + Function OtherMethod(optional x as Integer = 0) As Integer + return 0 + End Function + + Sub M() + Goo(OtherMethod) + End Sub + + Private Sub Goo(otherMethod As Integer) + Throw New NotImplementedException() + End Sub +End Class") + End Function + + + Public Async Function TestMethodReference5() As Task + Await TestInRegularAndScriptAsync( +"Class C + Sub OtherMethod() + return 0 + End Sub + + Sub M() + [|Goo|](OtherMethod) + End Sub +End Class", +"Imports System + +Class C + Sub OtherMethod() + return 0 + End Sub + + Sub M() + Goo(OtherMethod) + End Sub + + Private Sub Goo(otherMethod As Object) + Throw New NotImplementedException() + End Sub End Class") End Function End Class diff --git a/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb b/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb index dfa8b5066c47f..6d4898b7defff 100644 --- a/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb @@ -15,6 +15,7 @@ Imports Microsoft.CodeAnalysis.ExtractMethod Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.Text.Shared.Extensions +Imports Microsoft.CodeAnalysis.UnitTests Imports Microsoft.CodeAnalysis.VisualBasic.ExtractMethod Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.VisualStudio.Text @@ -115,10 +116,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ExtractMethod Assert.True(selectedCode.ContainsValidContext) ' extract method - Dim extractGenerationOptions = New ExtractMethodGenerationOptions(CodeGenerationOptions.GetDefault(document.Project.Services)) With - { - .ExtractOptions = extractOptions - } + Dim extractGenerationOptions = VBOptionsFactory.CreateExtractMethodGenerationOptions( + CodeGenerationOptions.GetDefault(document.Project.Services), + extractOptions) Dim extractor = New VisualBasicMethodExtractor(CType(selectedCode, VisualBasicSelectionResult), extractGenerationOptions) Dim result = Await extractor.ExtractMethodAsync(CancellationToken.None) diff --git a/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb b/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb index ad4d78a970195..6542b6a97ce1a 100644 --- a/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb +++ b/src/EditorFeatures/VisualBasicTest/NavigateTo/NavigateToTests.vb @@ -22,8 +22,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.NavigateTo Protected Overrides ReadOnly Property Language As String = "vb" - Protected Overrides Function CreateWorkspace(content As String, exportProvider As ExportProvider) As TestWorkspace - Return TestWorkspace.CreateVisualBasic(content, exportProvider:=exportProvider) + Protected Overrides Function CreateWorkspace(content As String, composition As TestComposition) As TestWorkspace + Return TestWorkspace.CreateVisualBasic(content, composition:=composition) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb b/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb index 879e6e7537728..0758f233cee0b 100644 --- a/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb @@ -132,7 +132,7 @@ End Class" Dim expectedToolTip = New ContainerElement( ContainerElementStyle.Wrapped, New ClassifiedTextElement( - New ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(new Uri("https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), + New ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0005", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(New Uri("https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005", UriKind.Absolute)), "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005"), New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), New ClassifiedTextRun(ClassificationTypeNames.Text, VisualBasicAnalyzersResources.Imports_statement_is_unnecessary))) @@ -145,7 +145,7 @@ End Class" expectedToolTip = New ContainerElement( ContainerElementStyle.Wrapped, New ClassifiedTextElement( - New ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0049", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(New Uri("https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049", UriKind.Absolute)), "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049"), + New ClassifiedTextRun(ClassificationTypeNames.Text, "IDE0049", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(New Uri("https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049", UriKind.Absolute)), "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049"), New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), New ClassifiedTextRun(ClassificationTypeNames.Text, AnalyzersResources.Name_can_be_simplified))) diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb b/src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb index af9f68c8b3be4..2dbc6192991b9 100644 --- a/src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb @@ -781,6 +781,46 @@ end class", b, c) end sub +end class") + End Function + + + Public Async Function TestMissingStartToken1() As Task + Await TestMissingAsync( +"class C + sub Bar() + Goobar [||]) + end sub +end class") + End Function + + + Public Async Function TestMissingStartToken2() As Task + Await TestMissingAsync( +"class C + sub Bar() + Goobar [||]1, 2) + end sub +end class") + End Function + + + Public Async Function TestMissingEndToken1() As Task + Await TestMissingAsync( +"class C + sub Bar() + Goobar([||] + end sub +end class") + End Function + + + Public Async Function TestMissingEndToken2() As Task + Await TestMissingAsync( +"class C + sub Bar() + Goobar([||]1, 2 + end sub end class") End Function End Class diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb b/src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb index 035aaa5f4853e..63e9a578ab994 100644 --- a/src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb @@ -428,5 +428,45 @@ End Sub", "Public Sub F }) End Sub") End Function + + + Public Async Function TestMissingStartToken1() As Task + Await TestMissingAsync( +"Class C + Public Sub Bar() + Dim test() As Integer = New Integer() [||] } + End Sub +End Class") + End Function + + + Public Async Function TestMissingStartToken2() As Task + Await TestMissingAsync( +"Class C + Public Sub Bar() + Dim test() As Integer = New Integer() [||]1, 2 } + End Sub +End Class") + End Function + + + Public Async Function TestMissingEndToken1() As Task + Await TestMissingAsync( +"Class C + Public Sub Bar() + Dim test() As Integer = New Integer() {[||] + End Sub +End Class") + End Function + + + Public Async Function TestMissingEndToken2() As Task + Await TestMissingAsync( +"Class C + Public Sub Bar() + Dim test() As Integer = New Integer() {[||]1, 2 + End Sub +End Class") + End Function End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb b/src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb index 8c2a09c7e460f..71bae2f01062c 100644 --- a/src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb @@ -790,6 +790,42 @@ end class") var v = [||]c => { end function end sub +end class") + End Function + + + Public Async Function TestMissingStartToken1() As Task + Await TestMissingAsync( +"class C + sub Goobar [||]) + end sub +end class") + End Function + + + Public Async Function TestMissingStartToken2() As Task + Await TestMissingAsync( +"class C + sub Goobar [||]i as integer, j as integer) + end sub +end class") + End Function + + + Public Async Function TestMissingEndToken1() As Task + Await TestMissingAsync( +"class C + sub Goobar([||] + end sub +end class") + End Function + + + Public Async Function TestMissingEndToken2() As Task + Await TestMissingAsync( +"class C + sub Goobar([||]i as integer, j as integer + end sub end class") End Function End Class diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/PlaceholderLocalBinder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/PlaceholderLocalBinder.cs index ada1d59f65b20..00f0ac418dc3d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/PlaceholderLocalBinder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Binders/PlaceholderLocalBinder.cs @@ -112,12 +112,12 @@ protected override ImmutableArray BuildLocals() internal override ImmutableArray GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj index d85865c649e76..6b03baa6e3f81 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj @@ -8,6 +8,7 @@ Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler true full + true diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/CapturedVariableRewriter.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/CapturedVariableRewriter.cs index 137fb1b98f63c..8390abf31e13d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/CapturedVariableRewriter.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/CapturedVariableRewriter.cs @@ -80,7 +80,7 @@ public override BoundNode VisitParameter(BoundParameter node) public override BoundNode VisitMethodGroup(BoundMethodGroup node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitThisReference(BoundThisReference node) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs index 49906e90df32a..1744b43756706 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs @@ -121,7 +121,7 @@ internal EEDisplayClassFieldSymbol(NamedTypeSymbol container, string name, TypeW public override Symbol AssociatedSymbol { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override Symbol ContainingSymbol @@ -131,12 +131,12 @@ public override Symbol ContainingSymbol public override Accessibility DeclaredAccessibility { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override ImmutableArray DeclaringSyntaxReferences { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override bool IsConst @@ -159,7 +159,7 @@ public override bool IsVolatile get { return false; } } - internal override bool IsRequired => throw ExceptionUtilities.Unreachable; + internal override bool IsRequired => throw ExceptionUtilities.Unreachable(); public override FlowAnalysisAnnotations FlowAnalysisAnnotations { @@ -168,7 +168,7 @@ public override FlowAnalysisAnnotations FlowAnalysisAnnotations public override ImmutableArray Locations { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override string Name @@ -178,37 +178,37 @@ public override string Name internal override bool HasRuntimeSpecialName { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool HasSpecialName { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool IsNotSerialized { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override MarshalPseudoCustomAttributeData MarshallingInformation { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override ObsoleteAttributeData ObsoleteAttributeData { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override int? TypeLayoutOffset { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override ConstantValue GetConstantValue(ConstantFieldsInProgress inProgress, bool earlyDecodingWellKnownAttributes) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEDisplayClassFieldLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEDisplayClassFieldLocalSymbol.cs index b83a057c4bc1d..5778967ea1841 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEDisplayClassFieldLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEDisplayClassFieldLocalSymbol.cs @@ -44,7 +44,7 @@ internal override LocalDeclarationKind DeclarationKind internal override SyntaxToken IdentifierToken { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override Symbol ContainingSymbol diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalConstantSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalConstantSymbol.cs index 7f057d2953154..07094798d7289 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalConstantSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalConstantSymbol.cs @@ -56,7 +56,7 @@ internal override LocalDeclarationKind DeclarationKind internal override SyntaxToken IdentifierToken { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override Symbol ContainingSymbol diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbol.cs index 0665f39efe691..9a5e22202b73b 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbol.cs @@ -98,7 +98,7 @@ public override string Name internal override SyntaxToken IdentifierToken { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override ImmutableArray DeclaringSyntaxReferences diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbolBase.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbolBase.cs index 1fd9b2db082cc..b43cf816751bb 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbolBase.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EELocalSymbolBase.cs @@ -54,7 +54,7 @@ internal override SyntaxNode ScopeDesignatorOpt internal sealed override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override bool IsImportedFromMetadata @@ -64,7 +64,7 @@ internal sealed override bool IsImportedFromMetadata internal override SyntaxNode GetDeclaratorSyntax() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal sealed override UseSiteInfo GetUseSiteInfo() diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index 9d16845728bbf..e3d75a234417c 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -253,7 +253,7 @@ public override bool AreLocalsZeroed internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation @@ -347,7 +347,7 @@ public override Symbol AssociatedSymbol internal override ImmutableArray GetAppliedConditionalSymbols() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override Cci.CallingConvention CallingConvention @@ -385,7 +385,7 @@ public override ImmutableArray Locations public override ImmutableArray DeclaringSyntaxReferences { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override Accessibility DeclaredAccessibility @@ -429,10 +429,10 @@ public override bool IsExtern internal override ObsoleteAttributeData ObsoleteAttributeData { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } - internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable; + internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable(); internal override bool HasUnscopedRefAttribute => false; @@ -727,6 +727,6 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l internal override bool IsNullableAnalysisEnabled() => false; - protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs index 883a9b46e2d9a..2bd6c9a780ad0 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs @@ -95,7 +95,7 @@ internal EENamedTypeSymbol( } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); internal ImmutableArray Methods { @@ -144,7 +144,7 @@ public override bool MightContainExtensionMethods internal override AttributeUsageInfo GetAttributeUsageInfo() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override string Name @@ -163,7 +163,7 @@ internal override bool MangleName public override IEnumerable MemberNames { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool HasDeclaredRequiredMembers => false; @@ -188,12 +188,12 @@ public override ImmutableArray GetTypeMembers() public override ImmutableArray GetTypeMembers(string name) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override ImmutableArray GetTypeMembers(string name, int arity) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override Accessibility DeclaredAccessibility @@ -203,12 +203,12 @@ public override Accessibility DeclaredAccessibility internal override ImmutableArray GetEarlyAttributeDecodingMembers() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetEarlyAttributeDecodingMembers(string name) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override NamedTypeSymbol GetDeclaredBaseType(ConsList basesBeingResolved) @@ -218,7 +218,7 @@ internal override NamedTypeSymbol GetDeclaredBaseType(ConsList bases internal override ImmutableArray GetDeclaredInterfaces(ConsList basesBeingResolved) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool HasSpecialName @@ -268,12 +268,12 @@ internal override bool HasDeclarativeSecurity internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetAppliedConditionalSymbols() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics @@ -283,7 +283,7 @@ internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList basesBeingResolved) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override TypeKind TypeKind @@ -348,7 +348,7 @@ internal override bool IsInterface internal override bool HasCodeAnalysisEmbeddedAttribute => false; - internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs index d6631399d085c..a994070a42802 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs @@ -46,7 +46,7 @@ public override string Name public override ImmutableArray DeclaringSyntaxReferences { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override bool HasConstructorConstraint @@ -110,7 +110,7 @@ public override bool HasUnmanagedTypeConstraint public override ImmutableArray Locations { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override int Ordinal @@ -120,7 +120,7 @@ public override int Ordinal public override TypeParameterKind TypeParameterKind { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override VarianceKind Variance diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs index c96fd444d5081..b302d783b4c8d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs @@ -88,7 +88,7 @@ internal override LocalDeclarationKind DeclarationKind internal override SyntaxToken IdentifierToken { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override TypeWithAnnotations TypeWithAnnotations @@ -137,7 +137,7 @@ internal override EELocalSymbolBase ToOtherMethod(MethodSymbol method, TypeMap t { // Placeholders should be rewritten (as method calls) // rather than copied as locals to the target method. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index a33b46c2883f2..20372abe82bdd 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -64,7 +64,7 @@ public override Accessibility DeclaredAccessibility public override ImmutableArray DeclaringSyntaxReferences { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override ImmutableArray ExplicitInterfaceImplementations @@ -213,10 +213,10 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute internal override ObsoleteAttributeData ObsoleteAttributeData { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } - internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable; + internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable(); internal override bool HasUnscopedRefAttribute => false; @@ -239,17 +239,17 @@ public override DllImportData GetDllImportData() public override bool AreLocalsZeroed { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override ImmutableArray GetAppliedConditionalSymbols() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsMetadataFinal @@ -272,12 +272,12 @@ internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChang internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsNullableAnalysisEnabled() => false; - protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); #if DEBUG protected override MethodSymbolAdapter CreateCciAdapter() diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs index 604bfbce2c7b8..87096027a1305 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs @@ -99,12 +99,12 @@ public override Symbol ContainingSymbol public override ImmutableArray Locations { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override ImmutableArray DeclaringSyntaxReferences { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override void EnsureAllConstraintsAreResolved() @@ -118,17 +118,17 @@ internal override ImmutableArray GetConstraintTypes(ConsLis internal override ImmutableArray GetInterfaces(ConsList inProgress) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override NamedTypeSymbol GetEffectiveBaseClass(ConsList inProgress) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override TypeSymbol GetDeducedBaseType(ConsList inProgress) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs index 5121e63935310..caa1c51382baf 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs @@ -104,7 +104,7 @@ public override bool IsVirtual public override ImmutableArray Locations { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override MethodKind MethodKind @@ -129,7 +129,7 @@ public override RefKind RefKind public override TypeWithAnnotations ReturnTypeWithAnnotations { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; @@ -138,7 +138,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override ImmutableArray RefCustomModifiers { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override ImmutableArray TypeArgumentsWithAnnotations @@ -153,52 +153,52 @@ public override ImmutableArray TypeParameters internal override Microsoft.Cci.CallingConvention CallingConvention { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool GenerateDebugInfo { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool HasDeclarativeSecurity { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool HasSpecialName { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override MethodImplAttributes ImplementationAttributes { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override bool RequiresSecurityObject { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation { - get { throw ExceptionUtilities.Unreachable; } + get { throw ExceptionUtilities.Unreachable(); } } public override DllImportData GetDllImportData() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray GetAppliedConditionalSymbols() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override IEnumerable GetSecurityInformation() { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsMetadataFinal @@ -211,14 +211,14 @@ internal override bool IsMetadataFinal internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } - protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); } } 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/CSharp/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj index b984824f83ed2..63f1996f5d9c0 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj +++ b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj @@ -7,6 +7,7 @@ Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider netstandard2.0 full + true diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs index dff2bae43b7f2..fb9bde96fa174 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs @@ -420,7 +420,7 @@ IntPtr gmdbpf(AssemblyIdentity assemblyIdentity, out uint uSize) else { Marshal.ThrowExceptionForHR(DkmExceptionUtilities.CORDBG_E_MISSING_METADATA); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -447,7 +447,7 @@ public void ShouldTryAgain_CORDBG_E_MISSING_METADATA() (AssemblyIdentity assemblyIdentity, out uint uSize) => { Marshal.ThrowExceptionForHR(DkmExceptionUtilities.CORDBG_E_MISSING_METADATA); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }); } @@ -458,7 +458,7 @@ public void ShouldTryAgain_COR_E_BADIMAGEFORMAT() (AssemblyIdentity assemblyIdentity, out uint uSize) => { Marshal.ThrowExceptionForHR(DkmExceptionUtilities.COR_E_BADIMAGEFORMAT); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }); } @@ -478,7 +478,7 @@ public void ShouldTryAgain_RPC_E_DISCONNECTED() IntPtr gmdbpf(AssemblyIdentity assemblyIdentity, out uint uSize) { Marshal.ThrowExceptionForHR(unchecked((int)0x80010108)); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } var references = ImmutableArray.Empty; @@ -765,7 +765,7 @@ class UseLinq block = systemCore.MetadataBlock; break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } uSize = (uint)block.Size; return block.Pointer; diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/WinMdTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/WinMdTests.cs index 592dc013fde8f..24b8a95837665 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/WinMdTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/WinMdTests.cs @@ -355,7 +355,7 @@ static void M(C c) { // Compilation should succeed without retry if we redirect assembly refs correctly. // Throwing so that we don't loop forever (as we did before fix)... - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }, out errorMessage, out testData); diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/DkmUtilities.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/DkmUtilities.cs index 4cf9996651254..79e999da8eb32 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/DkmUtilities.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/DkmUtilities.cs @@ -95,7 +95,7 @@ internal static ImmutableArray GetMetadataBlocks( ptr = runtime.GetIntrinsicAssemblyMetaDataBytesPtr(out size); if (!TryGetMetadataBlock(previousMetadataBlocks, index, ptr, size, out var intrinsicsBlock)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } builder.Add(intrinsicsBlock); diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/EEMetadataReferenceResolver.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/EEMetadataReferenceResolver.cs index f9aaa769bc5e2..ecc4b97ed3e95 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/EEMetadataReferenceResolver.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/EEMetadataReferenceResolver.cs @@ -46,13 +46,13 @@ internal EEMetadataReferenceResolver( } public override ImmutableArray ResolveReference(string reference, string? baseFilePath, MetadataReferenceProperties properties) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override bool Equals(object? other) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override int GetHashCode() - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); private (AssemblyIdentity? Identity, MetadataReference? Reference) GetBestMatch( ImmutableArray<(AssemblyIdentity Identity, MetadataReference Reference)> references, diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.cs index d44ca8f76d609..8c9285df0529f 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionCompiler.cs @@ -78,7 +78,7 @@ DkmCompiledClrLocalsQuery IDkmClrExpressionCompiler.GetClrLocalVariableQuery( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -136,7 +136,7 @@ void IDkmClrExpressionCompiler.CompileExpression( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -177,7 +177,7 @@ void IDkmClrExpressionCompiler.CompileAssignment( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -211,7 +211,7 @@ void IDkmClrExpressionCompilerCallback.CompileDisplayAttribute( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs index 5ccf6707691e8..f6e6c1ca2ab0c 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs @@ -58,7 +58,7 @@ void IDkmLanguageFrameDecoder.GetFrameName( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -79,7 +79,7 @@ void IDkmLanguageFrameDecoder.GetFrameReturnType( } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs index 42430a9887171..69429ca46ff4e 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs @@ -55,7 +55,7 @@ string IDkmLanguageInstructionDecoder.GetMethodName(DkmLanguageInstructionAddres } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/Microsoft.CodeAnalysis.ExpressionCompiler.csproj b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/Microsoft.CodeAnalysis.ExpressionCompiler.csproj index 18565ce67ea40..fcb75f123f323 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/Microsoft.CodeAnalysis.ExpressionCompiler.csproj +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/Microsoft.CodeAnalysis.ExpressionCompiler.csproj @@ -9,6 +9,7 @@ netstandard2.0 $(DefineConstants);EXPRESSIONCOMPILER full + true diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj index 8b9e968f1b1bd..01a16712df62d 100644 --- a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj +++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj @@ -7,6 +7,7 @@ Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver true netstandard2.0 + true diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/TypeHelpers.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/TypeHelpers.cs index 1864e8eee82d6..0191e42830e16 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/TypeHelpers.cs +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/Helpers/TypeHelpers.cs @@ -853,7 +853,7 @@ internal static MemberInfo GetOriginalDefinition(this MemberInfo member) } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } internal static Type GetInterfaceListEntry(this Type interfaceType, Type declaration) diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj b/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj index 740281bca5615..2577513d917e2 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj @@ -7,6 +7,7 @@ Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider netstandard2.0 full + true diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs index 22c3737825536..66576e7609fb3 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/ResultProvider.cs @@ -102,7 +102,7 @@ DkmClrValue IDkmClrResultProvider.GetClrValue(DkmSuccessEvaluationResult evaluat } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -205,7 +205,7 @@ string IDkmClrResultProvider.GetUnderlyingString(DkmEvaluationResult result) } catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs index b2cf692ad5453..9d7eaf7294d38 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs @@ -130,13 +130,13 @@ internal NamespaceTypeDefinitionNoBase(INamespaceTypeDefinition underlyingType) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable; + throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); } } } diff --git a/src/ExpressionEvaluator/Directory.Build.props b/src/ExpressionEvaluator/Directory.Build.props index 6eef643958f56..3ce37ebfedd4f 100644 --- a/src/ExpressionEvaluator/Directory.Build.props +++ b/src/ExpressionEvaluator/Directory.Build.props @@ -2,5 +2,8 @@ true + + + $(NoWarn);NU5123 diff --git a/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj b/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj index c820cc130ad54..e4c897b2cdbf8 100644 --- a/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj +++ b/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj @@ -4,6 +4,7 @@ Library net472 + false true diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj index 2470f1e44fa80..1cafbb6c40a82 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj @@ -6,6 +6,7 @@ Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ExpressionCompiler netstandard2.0 + true 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/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.VisualBasic.ResultProvider.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.VisualBasic.ResultProvider.vbproj index 459fd83d79d62..cbb1a4706b70d 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.VisualBasic.ResultProvider.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.VisualBasic.ResultProvider.vbproj @@ -6,6 +6,7 @@ Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider netstandard2.0 + true diff --git a/src/Features/CSharp/Portable/CodeFixes/GenerateMethod/GenerateDeconstructMethodCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/GenerateMethod/GenerateDeconstructMethodCodeFixProvider.cs index e14a940aae32f..077810358fea2 100644 --- a/src/Features/CSharp/Portable/CodeFixes/GenerateMethod/GenerateDeconstructMethodCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/GenerateMethod/GenerateDeconstructMethodCodeFixProvider.cs @@ -81,7 +81,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) target = assignment.Left; break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (type?.Kind != SymbolKind.NamedType) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs index 3d6c582623523..9c27cc06a833e 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs @@ -57,7 +57,7 @@ public CSharpChangeNamespaceService() } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (!IsSupportedLinkedDocument(document, out var allDocumentIds)) @@ -75,7 +75,7 @@ protected override string GetDeclaredNamespace(SyntaxNode container) if (container is BaseNamespaceDeclarationSyntax namespaceDecl) return CSharpSyntaxGenerator.Instance.GetName(namespaceDecl); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override SyntaxList GetMemberDeclarationsInContainer(SyntaxNode container) @@ -86,7 +86,7 @@ protected override SyntaxList GetMemberDeclarationsInCo if (container is CompilationUnitSyntax compilationUnit) return compilationUnit.Members; - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// @@ -256,7 +256,7 @@ protected override CompilationUnitSyntax ChangeNamespaceDeclaration( .WithoutAnnotations(ContainerAnnotation)); // Make sure to remove the annotation we added } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static CompilationUnitSyntax MoveMembersFromNamespaceToGlobal( diff --git a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs index 5d7b64c6b8c97..4ba905cc6c392 100644 --- a/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs +++ b/src/Features/CSharp/Portable/Completion/CSharpCompletionService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Completion @@ -18,21 +19,24 @@ internal sealed class CSharpCompletionService : CommonCompletionService [ExportLanguageServiceFactory(typeof(CompletionService), LanguageNames.CSharp), Shared] internal sealed class Factory : ILanguageServiceFactory { + private readonly IAsynchronousOperationListenerProvider _listenerProvider; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Factory() + public Factory(IAsynchronousOperationListenerProvider listenerProvider) { + _listenerProvider = listenerProvider; } [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => new CSharpCompletionService(languageServices.LanguageServices.SolutionServices); + => new CSharpCompletionService(languageServices.LanguageServices.SolutionServices, _listenerProvider); } private CompletionRules _latestRules = CompletionRules.Default; - private CSharpCompletionService(SolutionServices services) - : base(services) + private CSharpCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) + : base(services, listenerProvider) { } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs index b4bd66d9d0f5c..e96f0f6ffa8bb 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs @@ -66,7 +66,7 @@ internal static async Task GetDeclarationInfoAsync(Document || IsPropertyDeclaration(token, semanticModel, cancellationToken, out result) || IsPossibleOutVariableDeclaration(token, semanticModel, typeInferenceService, cancellationToken, out result) || IsTupleLiteralElement(token, semanticModel, cancellationToken, out result) - || IsPossibleVariableOrLocalMethodDeclaration(token, semanticModel, cancellationToken, out result) + || IsPossibleLocalVariableOrFunctionDeclaration(token, semanticModel, cancellationToken, out result) || IsPatternMatching(token, semanticModel, cancellationToken, out result)) { return result; @@ -154,7 +154,7 @@ private static bool IsPossibleOutVariableDeclaration(SyntaxToken token, Semantic return false; } - private static bool IsPossibleVariableOrLocalMethodDeclaration( + private static bool IsPossibleLocalVariableOrFunctionDeclaration( SyntaxToken token, SemanticModel semanticModel, CancellationToken cancellationToken, out NameDeclarationInfo result) { @@ -165,8 +165,18 @@ private static bool IsPossibleVariableOrLocalMethodDeclaration( _ => ImmutableArray.Create( new SymbolKindOrTypeKind(SymbolKind.Local), new SymbolKindOrTypeKind(MethodKind.LocalFunction)), - cancellationToken); - return result.Type != null; + cancellationToken, + out var expression); + + if (result.Type is null || expression is null) + return false; + + // we have something like `x.y $$`. + // + // For this to actually be the start of a local declaration or function x.y needs to bind to an actual + // type symbol, not just any arbitrary expression that might have a type (e.g. `Console.BackgroundColor $$). + var symbol = semanticModel.GetSymbolInfo(expression, cancellationToken).GetAnySymbol(); + return symbol is ITypeSymbol; } private static bool IsPropertyDeclaration(SyntaxToken token, SemanticModel semanticModel, @@ -254,22 +264,29 @@ private static NameDeclarationInfo IsLastTokenOfType( Func> possibleDeclarationComputer, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode { + return IsLastTokenOfType(token, semanticModel, typeSyntaxGetter, modifierGetter, possibleDeclarationComputer, cancellationToken, out _); + } + + private static NameDeclarationInfo IsLastTokenOfType( + SyntaxToken token, + SemanticModel semanticModel, + Func typeSyntaxGetter, + Func modifierGetter, + Func> possibleDeclarationComputer, + CancellationToken cancellationToken, + out SyntaxNode? typeSyntax) where TSyntaxNode : SyntaxNode + { + typeSyntax = null; if (!IsPossibleTypeToken(token)) - { return default; - } var target = token.GetAncestor(); if (target == null) - { return default; - } - var typeSyntax = typeSyntaxGetter(target); + typeSyntax = typeSyntaxGetter(target); if (typeSyntax == null || token != typeSyntax.GetLastToken()) - { return default; - } var modifiers = modifierGetter(target); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs index fa9e1a4aabeda..00f092e8d26ca 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs @@ -66,12 +66,15 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC return; } - // Do not show name suggestions for unbound "async" identifier. - // Most likely user is writing an async method, so name suggestion will just interfere him - if (context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) && - context.SemanticModel.GetSymbolInfo(context.TargetToken).GetAnySymbol() is null) + // Do not show name suggestions for unbound "async" or "yield" identifier. + // Most likely user is using it as keyword, so name suggestion will just interfere them + if (context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) || + context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.YieldKeyword)) { - return; + if (context.SemanticModel.GetSymbolInfo(context.TargetToken).GetAnySymbol() is null) + { + return; + } } var nameInfo = await NameDeclarationInfo.GetDeclarationInfoAsync(document, position, cancellationToken).ConfigureAwait(false); @@ -294,7 +297,7 @@ static void ProcessRules( var symbolKind = kind.SymbolKind.HasValue ? kind.SymbolKind.Value : kind.MethodKind.HasValue ? SymbolKind.Method : - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); var modifiers = declarationInfo.Modifiers; foreach (var rule in rules) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs index f5fb1814b5b46..753a5ec942409 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -99,7 +100,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -118,7 +119,7 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman // When true, this completion provider shows both the type (e.g. DayOfWeek) and its qualified members (e.g. // DayOfWeek.Friday). We set this to false for enum-like cases (static members of structs and classes) so we // only show the qualified members in these cases. - var showType = true; + var isEnumOrCompletionListType = true; var position = context.Position; var enclosingNamedType = semanticModel.GetEnclosingNamedType(position, cancellationToken); if (type.TypeKind != TypeKind.Enum) @@ -138,7 +139,7 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman // If this isn't an enum or marked with completionlist, also check if it contains static members of // a matching type. These 'enum-like' types have similar characteristics to enum completion, but do // not show the containing type as a separate item in completion. - showType = false; + isEnumOrCompletionListType = false; enumType = TryGetTypeWithStaticMembers(type); if (enumType == null) { @@ -164,7 +165,7 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman var symbol = alias ?? type; var sortText = symbol.Name; - if (showType) + if (isEnumOrCompletionListType) { context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText, @@ -188,7 +189,12 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman if (!field.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) continue; + // Use enum member name as an additional filter text, which would promote this item + // during matching when user types member name only, like "Red" instead of + // "Colors.Red" var memberDisplayName = $"{displayText}.{field.Name}"; + var additionalFilterTexts = ImmutableArray.Create(field.Name); + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: memberDisplayName, displayTextSuffix: "", @@ -196,7 +202,9 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman rules: CompletionItemRules.Default, contextPosition: position, sortText: $"{sortText}_{index:0000}", - filterText: memberDisplayName)); + filterText: memberDisplayName, + tags: WellKnownTagArrays.TargetTypeMatch) + .WithAdditionalFilterTexts(additionalFilterTexts)); } } else if (enclosingNamedType is not null) @@ -222,14 +230,24 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman continue; } - if (!SymbolEqualityComparer.Default.Equals(type, symbolType) - || !staticSymbol.IsAccessibleWithin(enclosingNamedType) - || !staticSymbol.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) + // We only show static properties/fields of compatible type if containing type is NOT marked with completionlist tag. + if (!isEnumOrCompletionListType && !SymbolEqualityComparer.Default.Equals(type, symbolType)) + { + continue; + } + + if (!staticSymbol.IsAccessibleWithin(enclosingNamedType) || + !staticSymbol.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) { continue; } + // Use member name as an additional filter text, which would promote this item + // during matching when user types member name only, like "Empty" instead of + // "ImmutableArray.Empty" var memberDisplayName = $"{displayText}.{staticSymbol.Name}"; + var additionalFilterTexts = ImmutableArray.Create(staticSymbol.Name); + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: memberDisplayName, displayTextSuffix: "", @@ -237,7 +255,9 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman rules: CompletionItemRules.Default, contextPosition: position, sortText: memberDisplayName, - filterText: memberDisplayName)); + filterText: memberDisplayName, + tags: WellKnownTagArrays.TargetTypeMatch) + .WithAdditionalFilterTexts(additionalFilterTexts)); } } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs index ad3728c43364f..3c6ad08bb35dc 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs @@ -233,7 +233,7 @@ protected override int GetTargetCaretPosition(SyntaxNode caretTarget) } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs index 6892597f80c23..e4a307e091ce2 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs @@ -113,7 +113,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) return GetMemberType(type, name: identifier.Identifier.ValueText, document, semanticModel, position); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } static ITypeSymbol? GetMemberType(ITypeSymbol? type, string name, Document document, SemanticModel semanticModel, int position) @@ -129,7 +129,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) { IPropertySymbol property => property.Type, IFieldSymbol field => field.Type, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs index df9d1f340dd49..0207d163bca8e 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs @@ -31,11 +31,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers [Shared] internal sealed class SnippetCompletionProvider : LSPCompletionProvider { - private static readonly HashSet s_builtInSnippets = new() + private static readonly HashSet s_builtInSnippetsWithoutReplacement = new() { - "#if", "#region", "Attribute", "checked", "class", "ctor", "cw", "do", "else", "enum", "equals", "Exception", - "for", "foreach", "forr", "if", "indexer", "interface", "invoke", "iterindex", "iterator", "lock", "mbox", - "namespace", "prop", "propa", "propdp", "propfull", "propg", "sim", "struct", "svm", "switch", "testc", "testm", + "#if", "#region", "Attribute", "checked", "ctor", "do", "else", "enum", "equals", "Exception", + "for", "forr", "indexer", "invoke", "iterindex", "iterator", "lock", "mbox", + "namespace", "propa", "propdp", "propfull", "propg", "sim", "svm", "switch", "testc", "testm", "try", "tryf", "unchecked", "unsafe", "using", "while", "~" }; @@ -159,7 +159,7 @@ private static ImmutableArray GetSnippetCompletionItems( var snippets = service.GetSnippetsIfAvailable(); if (context.CompletionOptions.ShouldShowNewSnippetExperience(context.Document)) { - snippets = snippets.Where(snippet => !s_builtInSnippets.Contains(snippet.Shortcut)); + snippets = snippets.Where(snippet => s_builtInSnippetsWithoutReplacement.Contains(snippet.Shortcut)); } if (isPreProcessorContext) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs index fc282d6765432..1c6b066253358 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Utilities; @@ -40,7 +39,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || - context.LeftToken.GetPreviousTokenIfTouchingWord(position).IsKind(SyntaxKind.RecordKeyword) || + context.IsRecordDeclarationContext(s_validModifiers, cancellationToken) || syntaxTree.IsTypeParameterConstraintStartContext(position, context.LeftToken); } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs index b5a86f2d306e9..6902dcbc1c172 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Utilities; @@ -38,7 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || - context.LeftToken.GetPreviousTokenIfTouchingWord(position).IsKind(SyntaxKind.RecordKeyword) || + context.IsRecordDeclarationContext(s_validModifiers, cancellationToken) || syntaxTree.IsTypeParameterConstraintStartContext(position, context.LeftToken); } } diff --git a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConverter.cs b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConverter.cs index f0d22d3b33ec2..3f40de9bc764a 100644 --- a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConverter.cs +++ b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConverter.cs @@ -91,7 +91,7 @@ private static QueryClauseSyntax CreateQueryClause(ExtendedSyntaxNode node) .WithCommentsFrom(node.ExtraLeadingComments, node.ExtraTrailingComments); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static FromClauseSyntax CreateFromClause( @@ -292,7 +292,7 @@ private ExpressionSyntax CreateLinqInvocationForExtendedNode( return CreateLinqInvocationForExtendedNode(selectExpression, ref extendedNodeIndex, ref receiver, ref hasForEachChild); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs index 4db4751dc8123..94e54adf2a8dd 100644 --- a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs +++ b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs @@ -158,7 +158,7 @@ static SyntaxTrivia[] GetTriviaFromNode(SyntaxNode node) var localDeclaration = (LocalDeclarationStatementSyntax)node; if (localDeclaration.Declaration.Variables.Count != 1) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return new IEnumerable[] { @@ -179,7 +179,7 @@ static SyntaxTrivia[] GetTriviaFromNode(SyntaxNode node) break; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/CSharp/Portable/ConvertProgram/ConvertProgramTransform_TopLevelStatements.cs b/src/Features/CSharp/Portable/ConvertProgram/ConvertProgramTransform_TopLevelStatements.cs index 158fb8942fa59..3ad665fe3e564 100644 --- a/src/Features/CSharp/Portable/ConvertProgram/ConvertProgramTransform_TopLevelStatements.cs +++ b/src/Features/CSharp/Portable/ConvertProgram/ConvertProgramTransform_TopLevelStatements.cs @@ -207,7 +207,7 @@ private static ImmutableArray GetGlobalStatements( else if (member is not FieldDeclarationSyntax) { // checked by analyzer - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordCodeFixProvider.cs index f96167ff6e7f9..9ad658968b0c6 100644 --- a/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordCodeFixProvider.cs @@ -40,11 +40,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var span = context.Span; var cancellationToken = context.CancellationToken; - if (!context.Options.GetOptions(document.Project.Services).EnableConvertToRecord) - { - return; - } - // get the class declaration. The span should be on the base type in the base list var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var baseTypeSyntax = root.FindNode(span) as BaseTypeSyntax; diff --git a/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordRefactoringProvider.cs index 0be97bfd561ff..8ae7f91430ce6 100644 --- a/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRecord/CSharpConvertToRecordRefactoringProvider.cs @@ -24,11 +24,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte { var (document, _, cancellationToken) = context; - if (!context.Options.GetOptions(document.Project.Services).EnableConvertToRecord) - { - return; - } - var typeDeclaration = await context.TryGetRelevantNodeAsync().ConfigureAwait(false); if (typeDeclaration == null) { diff --git a/src/Features/CSharp/Portable/ConvertToRecord/PositionalParameterInfo.cs b/src/Features/CSharp/Portable/ConvertToRecord/PositionalParameterInfo.cs index 55619f381b0f1..53563739ce772 100644 --- a/src/Features/CSharp/Portable/ConvertToRecord/PositionalParameterInfo.cs +++ b/src/Features/CSharp/Portable/ConvertToRecord/PositionalParameterInfo.cs @@ -66,7 +66,7 @@ public static ImmutableArray GetPropertiesForPositional => new PositionalParameterInfo(syntax, symbol, !allowSetToInitConversion), ConvertStatus.AlwaysConvert => new PositionalParameterInfo(syntax, symbol, KeepAsOverride: false), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }).WhereNotNull()); return resultBuilder.ToImmutable(); diff --git a/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs b/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs index dd365a8df7b3b..d5b7ef2bad5e4 100644 --- a/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs +++ b/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs @@ -160,7 +160,7 @@ private static IEnumerable GetExceptions(SyntaxNode member) { ThrowExpressionSyntax throwExpression => throwExpression.Expression, ThrowStatementSyntax throwStatement => throwStatement.Expression, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; if (expression.IsKind(SyntaxKind.NullLiteralExpression)) diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs index 2f6c17b5e2d19..1f0a3dc3b64c9 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs @@ -54,7 +54,7 @@ public override bool ContainingScopeHasAsyncKeyword() { if (GetContainingScope() is not ExpressionSyntax node) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } var model = SemanticDocument.SemanticModel; diff --git a/src/Features/CSharp/Portable/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs b/src/Features/CSharp/Portable/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs index fb46267252d0c..541446b8c7931 100644 --- a/src/Features/CSharp/Portable/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs +++ b/src/Features/CSharp/Portable/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs @@ -131,12 +131,7 @@ private ITypeParameterSymbol GetMethodTypeParameter(TypeSyntax type, Cancellatio } protected override ImmutableArray DetermineParameterModifiers(CancellationToken cancellationToken) - { - return - _invocationExpression.ArgumentList.Arguments.Select( - a => a.RefOrOutKeyword.Kind() == SyntaxKind.RefKeyword ? RefKind.Ref : - a.RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword ? RefKind.Out : RefKind.None).ToImmutableArray(); - } + => _invocationExpression.ArgumentList.Arguments.Select(a => a.GetRefKind()).ToImmutableArray(); protected override ImmutableArray DetermineParameterTypes(CancellationToken cancellationToken) => _invocationExpression.ArgumentList.Arguments.Select(a => DetermineParameterType(a, cancellationToken)).ToImmutableArray(); diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs index 61ffdc107e6a4..8d985cc21f9f5 100644 --- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs +++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs @@ -321,7 +321,7 @@ fieldDeclaration.Parent is CompilationUnitSyntax && return true; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } else { @@ -715,7 +715,7 @@ internal override bool IsPublicOnlyAccessibility(ExpressionSyntax expression, Pr } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if ((node is EventDeclarationSyntax || node is EventFieldDeclarationSyntax) && diff --git a/src/Features/CSharp/Portable/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs b/src/Features/CSharp/Portable/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs index 417214bb7d8b4..2c4084a559032 100644 --- a/src/Features/CSharp/Portable/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs @@ -44,7 +44,7 @@ protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node) MethodDeclarationSyntax method => method.WithBody(block).WithExpressionBody(null).WithSemicolonToken(default), LocalFunctionStatementSyntax localFunction => localFunction.WithBody(block).WithExpressionBody(null).WithSemicolonToken(default), AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.WithBody(block).WithExpressionBody(null), - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } diff --git a/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs index 01b76a10461a4..b4e491e267189 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs @@ -129,7 +129,7 @@ private static TextSpan GetTextSpan(ExpressionSyntax expression, SyntaxToken ope return IncompleteElementAccessExpression.GetTextSpan(expression, openBracket); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static SignatureHelpState? GetCurrentArgumentState(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs index aa81d7f827a6f..1510b17f71f21 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs @@ -136,7 +136,7 @@ private static bool IsArgumentListToken(InvocationExpressionSyntax expression, S } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } if (currentSymbol is null) diff --git a/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs index 5e73eb0593084..a5d4987ed573f 100644 --- a/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs +++ b/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs @@ -62,7 +62,7 @@ protected override async Task GenerateSnippetSyntaxAsync(Document do protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText) { var propertyDeclaration = (PropertyDeclarationSyntax)caretTarget; - return propertyDeclaration.AccessorList!.OpenBraceToken.Span.End; + return propertyDeclaration.AccessorList!.CloseBraceToken.Span.End; } protected override ImmutableArray GetPlaceHolderLocationsList(SyntaxNode node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs index d2c6f1bbb94c8..cead1396f9cd2 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs @@ -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. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -10,12 +11,13 @@ using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SplitStringLiteral { internal abstract partial class StringSplitter { - protected static readonly SyntaxAnnotation RightNodeAnnotation = new(); + protected readonly SyntaxAnnotation RightNodeAnnotation = new(); protected static readonly SyntaxToken PlusNewLineToken = SyntaxFactory.Token( leading: default, diff --git a/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs b/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs index 9d5c1b789a5fa..2b6616c2c56fb 100644 --- a/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs +++ b/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs @@ -56,7 +56,7 @@ protected override void AppendTaskListItems( return; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override string GetNormalizedText(string message) diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs index 2401cb1decb3d..0773623b975e6 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; @@ -35,6 +36,12 @@ protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions optio protected override bool ShouldMoveCloseBraceToNewLine => false; + protected override SyntaxToken FirstToken(BaseArgumentListSyntax listSyntax) + => listSyntax.GetOpenToken(); + + protected override SyntaxToken LastToken(BaseArgumentListSyntax listSyntax) + => listSyntax.GetCloseToken(); + protected override SeparatedSyntaxList GetListItems(BaseArgumentListSyntax listSyntax) => listSyntax.Arguments; diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs index 63d15c6a16377..34586cc30e8a9 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs @@ -23,9 +23,9 @@ internal sealed partial class CSharpInitializerExpressionWrapper // unreachable as we explicitly declare that we don't support these scenarios. - protected override string Align_wrapped_items => throw ExceptionUtilities.Unreachable; - protected override string Indent_wrapped_items => throw ExceptionUtilities.Unreachable; - protected override string Unwrap_and_indent_all_items => throw ExceptionUtilities.Unreachable; + protected override string Align_wrapped_items => throw ExceptionUtilities.Unreachable(); + protected override string Indent_wrapped_items => throw ExceptionUtilities.Unreachable(); + protected override string Unwrap_and_indent_all_items => throw ExceptionUtilities.Unreachable(); protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options) => ((CSharpSyntaxWrappingOptions)options).NewLinesForBracesInObjectCollectionArrayInitializers; @@ -33,6 +33,12 @@ protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions optio protected override bool ShouldMoveCloseBraceToNewLine => true; + protected override SyntaxToken FirstToken(InitializerExpressionSyntax listSyntax) + => listSyntax.OpenBraceToken; + + protected override SyntaxToken LastToken(InitializerExpressionSyntax listSyntax) + => listSyntax.CloseBraceToken; + protected override SeparatedSyntaxList GetListItems(InitializerExpressionSyntax listSyntax) => listSyntax.Expressions; diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs index 862939dd857c7..3889f8b729733 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs @@ -35,6 +35,12 @@ protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions optio protected override bool ShouldMoveCloseBraceToNewLine => false; + protected override SyntaxToken FirstToken(BaseParameterListSyntax listSyntax) + => listSyntax.GetOpenToken(); + + protected override SyntaxToken LastToken(BaseParameterListSyntax listSyntax) + => listSyntax.GetCloseToken(); + protected override SeparatedSyntaxList GetListItems(BaseParameterListSyntax listSyntax) => listSyntax.Parameters; diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 607e86618fe63..b8e8e187ddbdc 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -262,7 +262,7 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( using var nestedTokenSource = new CancellationTokenSource(); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken); - foreach (var (referenceProjectId, reference) in newReferences) + foreach (var (referenceProject, reference) in newReferences) { var compilation = referenceToCompilation.GetOrAdd( reference, r => CreateCompilation(project, r)); @@ -272,7 +272,7 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { findTasks.Add(finder.FindInMetadataSymbolsAsync( - assembly, referenceProjectId, reference, exact, linkedTokenSource.Token)); + assembly, referenceProject, reference, exact, linkedTokenSource.Token)); } } @@ -284,10 +284,10 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( /// by this project. The set returned will be tuples containing the PEReference, and the project-id /// for the project we found the pe-reference in. /// - private static ImmutableArray<(ProjectId, PortableExecutableReference)> GetUnreferencedMetadataReferences( + private static ImmutableArray<(Project, PortableExecutableReference)> GetUnreferencedMetadataReferences( Project project, HashSet seenReferences) { - var result = ArrayBuilder<(ProjectId, PortableExecutableReference)>.GetInstance(); + var result = ArrayBuilder<(Project, PortableExecutableReference)>.GetInstance(); var solution = project.Solution; foreach (var p in solution.Projects) @@ -303,7 +303,7 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( !IsInPackagesDirectory(peReference) && seenReferences.Add(peReference)) { - result.Add((p.Id, peReference)); + result.Add((p, peReference)); } } } @@ -598,7 +598,7 @@ public ImmutableArray GetCodeActionsForFixes( AddImportFixKind.PackageSymbol => installerService?.IsInstalled(document.Project.Id, fixData.PackageName) == false ? new ParentInstallPackageCodeAction(document, fixData, installerService) : null, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; private static ITypeSymbol? GetAwaitInfo(SemanticModel semanticModel, ISyntaxFacts syntaxFactsService, SyntaxNode node) diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs index 06a3385c7b41f..769ae7b42d55d 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.AddImport { @@ -16,24 +17,21 @@ internal abstract partial class AbstractAddImportFeatureService provider, - Solution solution, + Project assemblyProject, IAssemblySymbol assembly, - ProjectId assemblyProjectId, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) : base(provider, exact, cancellationToken) { - _solution = solution; + _assemblyProject = assemblyProject; _assembly = assembly; - _assemblyProjectId = assemblyProjectId; _metadataReference = metadataReference; } @@ -42,15 +40,15 @@ public override SymbolReference CreateReference(SymbolResult searchResult) return new MetadataSymbolReference( provider, searchResult.WithSymbol(searchResult.Symbol), - _assemblyProjectId, + _assemblyProject.Id, _metadataReference); } protected override async Task> FindDeclarationsAsync( SymbolFilter filter, SearchQuery searchQuery) { - var service = _solution.Services.GetService(); - var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); + var service = _assemblyProject.Solution.Services.GetService(); + var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_assemblyProject, _metadataReference, CancellationToken).ConfigureAwait(false); if (info == null) return ImmutableArray.Empty; diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs index defb7b473b427..f0bf16c9a863e 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs @@ -36,7 +36,7 @@ public SourceSymbolsProjectSearchScope( protected override async Task> FindDeclarationsAsync( SymbolFilter filter, SearchQuery searchQuery) { - var service = _project.Solution.Services.GetService(); + var service = _project.Solution.Services.GetService(); var info = await service.TryGetPotentiallyStaleSourceSymbolTreeInfoAsync(_project, CancellationToken).ConfigureAwait(false); if (info == null) { diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 33aba1ead62cf..63f0d03d3f4f6 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs @@ -105,12 +105,11 @@ internal Task> FindInSourceSymbolsInProjectAsync } internal Task> FindInMetadataSymbolsAsync( - IAssemblySymbol assembly, ProjectId assemblyProjectId, PortableExecutableReference metadataReference, + IAssemblySymbol assembly, Project assemblyProject, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) { var searchScope = new MetadataSymbolsSearchScope( - _owner, _document.Project.Solution, assembly, assemblyProjectId, - metadataReference, exact, cancellationToken); + _owner, assemblyProject, assembly, metadataReference, exact, cancellationToken); return DoAsync(searchScope); } diff --git a/src/Features/Core/Portable/BraceCompletion/AbstractBraceCompletionService.cs b/src/Features/Core/Portable/BraceCompletion/AbstractBraceCompletionService.cs index 4ff9f3082fad9..92092d299b27f 100644 --- a/src/Features/Core/Portable/BraceCompletion/AbstractBraceCompletionService.cs +++ b/src/Features/Core/Portable/BraceCompletion/AbstractBraceCompletionService.cs @@ -107,7 +107,7 @@ public virtual bool CanProvideBraceCompletion(char brace, int openingPosition, P protected virtual Task IsValidOpenBraceTokenAtPositionAsync(Document document, SyntaxToken token, int position, CancellationToken cancellationToken) { // Subclass should have overridden this. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index f99d897054e36..94aa66f832b01 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -192,7 +192,7 @@ internal async Task ChangeSignatureWithContextAsync(Chang { ChangeSignatureAnalysisSucceededContext changeSignatureAnalyzedSucceedContext => await GetChangeSignatureResultAsync(changeSignatureAnalyzedSucceedContext, options, cancellationToken).ConfigureAwait(false), CannotChangeSignatureAnalyzedContext cannotChangeSignatureAnalyzedContext => new ChangeSignatureResult(succeeded: false, changeSignatureFailureKind: cannotChangeSignatureAnalyzedContext.CannotChangeSignatureReason), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; async Task GetChangeSignatureResultAsync(ChangeSignatureAnalysisSucceededContext context, ChangeSignatureOptionsResult? options, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ChangeSignature/Parameter.cs b/src/Features/Core/Portable/ChangeSignature/Parameter.cs index b61a0ca2042e6..58ae2385a8a69 100644 --- a/src/Features/Core/Portable/ChangeSignature/Parameter.cs +++ b/src/Features/Core/Portable/ChangeSignature/Parameter.cs @@ -68,7 +68,7 @@ public AddedParameter( CallSiteValue = FeaturesResources.ChangeSignature_NewParameterInferValue; break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs index 51d3074f6ef49..a2d1ef502c60e 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs @@ -305,19 +305,19 @@ 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. AnalyzerConfigDocument? analyzerConfigDocument = null; - var analyzerConfigDirectory = PathUtilities.GetDirectoryName(analyzerConfigPath) ?? throw ExceptionUtilities.Unreachable; + var analyzerConfigDirectory = PathUtilities.GetDirectoryName(analyzerConfigPath) ?? throw ExceptionUtilities.Unreachable(); var currentSolution = _project.Solution; foreach (var projectId in _project.Solution.ProjectIds) { 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/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs index d76e4d044ff66..cb08906a7f22f 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs @@ -65,7 +65,7 @@ await fixAllContext.GetProjectDiagnosticsToFixAsync().ConfigureAwait(false), return await batchFixer.GetFixAsync(fixAllContext).ConfigureAwait(false); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs index 5182ccf34e6d2..3c6a25a4978ec 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs @@ -120,7 +120,7 @@ private State( } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Namespace can't be changed if we can't construct a valid qualified identifier from folder names. diff --git a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs index b9f1f4ecdfeb5..c6b358f3a3b5e 100644 --- a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs +++ b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs @@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { @@ -29,27 +28,44 @@ internal abstract class AbstractProjectExtensionProvider GetLanguages(TExportAttribute exportAttribute); protected abstract bool TryGetExtensionsFromReference(AnalyzerReference reference, out ImmutableArray extensions); + public static bool TryGetCachedExtensions(IReadOnlyList analyzerReferences, out ImmutableArray extensions) + { + if (s_referencesToExtensionsMap.TryGetValue(analyzerReferences, out var providers)) + { + extensions = providers.Value; + return true; + } + + extensions = ImmutableArray.Empty; + return false; + } + public static ImmutableArray GetExtensions(Project? project) { if (project is null) return ImmutableArray.Empty; - if (s_referencesToExtensionsMap.TryGetValue(project.AnalyzerReferences, out var providers)) - return providers.Value; + return GetExtensions(project.Language, project.AnalyzerReferences); + } + + public static ImmutableArray GetExtensions(string language, IReadOnlyList analyzerReferences) + { + if (TryGetCachedExtensions(analyzerReferences, out var providers)) + return providers; - return GetExtensionsSlow(project); + return GetExtensionsSlow(language, analyzerReferences); - ImmutableArray GetExtensionsSlow(Project project) - => s_referencesToExtensionsMap.GetValue(project.AnalyzerReferences, _ => new(ComputeExtensions(project))).Value; + static ImmutableArray GetExtensionsSlow(string language, IReadOnlyList analyzerReferences) + => s_referencesToExtensionsMap.GetValue(analyzerReferences, _ => new(ComputeExtensions(language, analyzerReferences))).Value; - ImmutableArray ComputeExtensions(Project project) + static ImmutableArray ComputeExtensions(string language, IReadOnlyList analyzerReferences) { using var _ = ArrayBuilder.GetInstance(out var builder); - foreach (var reference in project.AnalyzerReferences) + foreach (var reference in analyzerReferences) { var provider = s_referenceToProviderMap.GetValue( reference, static reference => new TProvider() { Reference = reference }); - foreach (var extension in provider.GetExtensions(project.Language)) + foreach (var extension in provider.GetExtensions(language)) builder.Add(extension); } diff --git a/src/Features/Core/Portable/Common/TaggedText.cs b/src/Features/Core/Portable/Common/TaggedText.cs index 28c1d97bc3e3b..08ea0a8950396 100644 --- a/src/Features/Core/Portable/Common/TaggedText.cs +++ b/src/Features/Core/Portable/Common/TaggedText.cs @@ -11,6 +11,7 @@ using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -20,7 +21,7 @@ namespace Microsoft.CodeAnalysis /// A piece of text with a descriptive tag. /// [DataContract] - public readonly struct TaggedText + public readonly record struct TaggedText { /// /// A descriptive tag from . diff --git a/src/Features/Core/Portable/Completion/CommonCompletionService.cs b/src/Features/Core/Portable/Completion/CommonCompletionService.cs index 333486c21bf99..84f325956a85a 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionService.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionService.cs @@ -6,14 +6,15 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PatternMatching; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Tags; namespace Microsoft.CodeAnalysis.Completion { internal abstract partial class CommonCompletionService : CompletionService { - protected CommonCompletionService(SolutionServices services) - : base(services) + protected CommonCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) + : base(services, listenerProvider) { } diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 78407cff706cc..775f2e4aabe5d 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -81,46 +81,9 @@ public MatchResult GetMatchResult( matchedAdditionalFilterText); } - private PatternMatch? GetMatch( - string text, - string pattern, - bool includeMatchSpans, - CultureInfo culture) - { - // If the item has a dot in it (i.e. for something like enum completion), then attempt - // to match what the user wrote against the last portion of the name. That way if they - // write "Bl" and we have "Blub" and "Color.Black", we'll consider the latter to be a - // better match as they'll both be prefix matches, and the latter will have a higher - // priority. - - var lastDotIndex = text.LastIndexOf('.'); - if (lastDotIndex >= 0) - { - var afterDotPosition = lastDotIndex + 1; - var textAfterLastDot = text[afterDotPosition..]; - - var match = GetMatchWorker(textAfterLastDot, pattern, culture, includeMatchSpans); - if (match != null) - { - return AdjustMatchedSpans(match.Value, afterDotPosition); - } - } - - // Didn't have a dot, or the user text didn't match the portion after the dot. - // Just do a normal check against the entire completion item. - return GetMatchWorker(text, pattern, culture, includeMatchSpans); - } - - private static PatternMatch? AdjustMatchedSpans(PatternMatch value, int offset) - => value.MatchedSpans.IsDefaultOrEmpty - ? value - : value.WithMatchedSpans(value.MatchedSpans.SelectAsArray(s => new TextSpan(s.Start + offset, s.Length))); - - private PatternMatch? GetMatchWorker( - string text, string pattern, - CultureInfo culture, bool includeMatchSpans) + private PatternMatch? GetMatch(string text, string pattern, bool includeMatchSpans, CultureInfo culture) { - var patternMatcher = GetPatternMatcher(pattern, culture, includeMatchSpans); + var patternMatcher = GetPatternMatcher(pattern, culture, includeMatchSpans, _patternMatcherMap); var match = patternMatcher.GetFirstMatch(text); // We still have making checks for language having different to English capitalization, @@ -135,7 +98,7 @@ public MatchResult GetMatchResult( // Keywords in .NET are always in En-US. // Identifiers can be in user language. // Try to get matches for both and return the best of them. - patternMatcher = GetPatternMatcher(pattern, EnUSCultureInfo, includeMatchSpans); + patternMatcher = GetPatternMatcher(pattern, EnUSCultureInfo, includeMatchSpans, _patternMatcherMap); var enUSCultureMatch = patternMatcher.GetFirstMatch(text); if (match == null) @@ -170,9 +133,6 @@ private PatternMatcher GetPatternMatcher( } } - private PatternMatcher GetPatternMatcher(string pattern, CultureInfo culture, bool includeMatchedSpans) - => GetPatternMatcher(pattern, culture, includeMatchedSpans, _patternMatcherMap); - public int CompareMatchResults(MatchResult matchResult1, MatchResult matchResult2, bool filterTextHasNoUpperCase) { var item1 = matchResult1.CompletionItem; @@ -211,16 +171,13 @@ public int CompareMatchResults(MatchResult matchResult1, MatchResult matchResult } return 0; - } - - private static bool TagsEqual(CompletionItem item1, CompletionItem item2) - => TagsEqual(item1.Tags, item2.Tags); - private static bool TagsEqual(ImmutableArray tags1, ImmutableArray tags2) - => tags1 == tags2 || System.Linq.Enumerable.SequenceEqual(tags1, tags2); + static bool IsKeywordItem(CompletionItem item) + => item.Tags.Contains(WellKnownTags.Keyword); - private static bool IsKeywordItem(CompletionItem item) - => item.Tags.Contains(WellKnownTags.Keyword); + static bool TagsEqual(CompletionItem item1, CompletionItem item2) + => System.Linq.ImmutableArrayExtensions.SequenceEqual(item1.Tags, item2.Tags); + } private static int CompareItems( PatternMatch match1, @@ -422,9 +379,6 @@ private static int CompareExpandedItem(CompletionItem item1, PatternMatch match1 return isItem1Expanded ? -1 : 1; } - public static string ConcatNamespace(string? containingNamespace, string name) - => string.IsNullOrEmpty(containingNamespace) ? name : containingNamespace + "." + name; - internal static bool TryCreateMatchResult( CompletionHelper completionHelper, CompletionItem item, diff --git a/src/Features/Core/Portable/Completion/CompletionOptions.cs b/src/Features/Core/Portable/Completion/CompletionOptions.cs index aa475822fa99b..4f85a138385a5 100644 --- a/src/Features/Core/Portable/Completion/CompletionOptions.cs +++ b/src/Features/Core/Portable/Completion/CompletionOptions.cs @@ -30,7 +30,7 @@ internal sealed record class CompletionOptions public bool FilterOutOfScopeLocals { get; init; } = true; public bool ShowXmlDocCommentCompletion { get; init; } = true; public bool? ShowNewSnippetExperience { get; init; } = null; - public bool SnippetCompletion { get; init; } = false; + public bool SnippetCompletion { get; init; } = true; public ExpandedCompletionMode ExpandedCompletionBehavior { get; init; } = ExpandedCompletionMode.AllItems; public NamingStylePreferences? NamingStyleFallbackOptions { get; init; } = null; diff --git a/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs b/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs index 54f0e919a7f2d..46ff679d58d2a 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs @@ -8,11 +8,13 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -25,14 +27,22 @@ private sealed class ProviderManager : IEqualityComparer _nameToProvider = new(); private readonly Dictionary, ImmutableArray> _rolesToProviders; - private IReadOnlyList>? _lazyImportedProviders; private readonly CompletionService _service; - public ProviderManager(CompletionService service) + private readonly AsyncBatchingWorkQueue> _projectProvidersWorkQueue; + + public ProviderManager(CompletionService service, IAsynchronousOperationListenerProvider listenerProvider) { _service = service; _rolesToProviders = new Dictionary, ImmutableArray>(this); + + _projectProvidersWorkQueue = new AsyncBatchingWorkQueue>( + TimeSpan.FromSeconds(1), + ProcessBatchAsync, + EqualityComparer>.Default, + listenerProvider.GetListener(FeatureAttribute.CompletionSet), + CancellationToken.None); } public IReadOnlyList> GetLazyImportedProviders() @@ -53,7 +63,20 @@ public IReadOnlyList> GetLa return _lazyImportedProviders; } - public static ImmutableArray GetProjectCompletionProviders(Project? project) + private ValueTask ProcessBatchAsync(ImmutableSegmentedList> referencesList, CancellationToken cancellationToken) + { + foreach (var references in referencesList) + { + cancellationToken.ThrowIfCancellationRequested(); + // Go through the potentially slow path to ensure project providers are loaded. + // We only do this in background here to avoid UI delays. + _ = ProjectCompletionProvider.GetExtensions(_service.Language, references); + } + + return ValueTaskFactory.CompletedTask; + } + + public ImmutableArray GetCachedProjectCompletionProvidersOrQueueLoadInBackground(Project? project) { if (project is null || project.Solution.WorkspaceKind == WorkspaceKind.Interactive) { @@ -61,7 +84,15 @@ public static ImmutableArray GetProjectCompletionProviders(P return ImmutableArray.Empty; } - return ProjectCompletionProvider.GetExtensions(project); + // Don't load providers if they are not already cached, + // return immediately and load them in background instead. + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1620947 + + if (ProjectCompletionProvider.TryGetCachedExtensions(project.AnalyzerReferences, out var providers)) + return providers; + + _projectProvidersWorkQueue.AddWork(project.AnalyzerReferences); + return ImmutableArray.Empty; } private ImmutableArray GetImportedAndBuiltInProviders(ImmutableHashSet? roles) @@ -111,14 +142,14 @@ ImmutableArray GetImportedAndBuiltInProvidersWorker(Immutabl } using var _ = PooledDelegates.GetPooledFunction(static (p, n) => p.Name == n, item.ProviderName, out Func isNameMatchingProviderPredicate); - return GetProjectCompletionProviders(project).FirstOrDefault(isNameMatchingProviderPredicate); + return GetCachedProjectCompletionProvidersOrQueueLoadInBackground(project).FirstOrDefault(isNameMatchingProviderPredicate); } public ConcatImmutableArray GetFilteredProviders( Project? project, ImmutableHashSet? roles, CompletionTrigger trigger, in CompletionOptions options) { var allCompletionProviders = FilterProviders(GetImportedAndBuiltInProviders(roles), trigger, options); - var projectCompletionProviders = FilterProviders(GetProjectCompletionProviders(project), trigger, options); + var projectCompletionProviders = FilterProviders(GetCachedProjectCompletionProvidersOrQueueLoadInBackground(project), trigger, options); return allCompletionProviders.ConcatFast(projectCompletionProviders); } @@ -231,12 +262,17 @@ public TestAccessor(ProviderManager providerManager) _providerManager = providerManager; } - public ImmutableArray GetProviders(ImmutableHashSet roles, Project? project) + public ImmutableArray GetImportedAndBuiltInProviders(ImmutableHashSet roles) { - using var _ = ArrayBuilder.GetInstance(out var providers); - providers.AddRange(_providerManager.GetImportedAndBuiltInProviders(roles)); - providers.AddRange(GetProjectCompletionProviders(project)); - return providers.ToImmutable(); + return _providerManager.GetImportedAndBuiltInProviders(roles); + } + + public async Task> GetProjectProvidersAsync(Project project) + { + _providerManager._projectProvidersWorkQueue.AddWork(project.AnalyzerReferences); + await _providerManager._projectProvidersWorkQueue.WaitUntilCurrentBatchCompletesAsync().ConfigureAwait(false); + // Now the extension cache is guaranteed to be populated. + return _providerManager.GetCachedProjectCompletionProvidersOrQueueLoadInBackground(project); } } } diff --git a/src/Features/Core/Portable/Completion/CompletionService.cs b/src/Features/Core/Portable/Completion/CompletionService.cs index 9ef66a86dd0e8..6c21c8eed6072 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -38,10 +39,10 @@ public abstract partial class CompletionService : ILanguageService private bool _suppressPartialSemantics; // Prevent inheritance outside of Roslyn. - internal CompletionService(SolutionServices services) + internal CompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) { _services = services; - _providerManager = new(this); + _providerManager = new(this, listenerProvider); } /// @@ -338,10 +339,10 @@ internal IReadOnlyList> Get => _providerManager.GetLazyImportedProviders(); /// - /// Don't call. Used for pre-populating NuGet providers only. + /// Don't call. Used for pre-load project providers only. /// - internal static ImmutableArray GetProjectCompletionProviders(Project project) - => ProviderManager.GetProjectCompletionProviders(project); + internal void TriggerLoadProjectProviders(Project project) + => _providerManager.GetCachedProjectCompletionProvidersOrQueueLoadInBackground(project); internal CompletionProvider? GetProvider(CompletionItem item, Project? project) => _providerManager.GetProvider(item, project); @@ -356,10 +357,13 @@ internal readonly struct TestAccessor public TestAccessor(CompletionService completionServiceWithProviders) => _completionServiceWithProviders = completionServiceWithProviders; - internal ImmutableArray GetAllProviders(ImmutableHashSet roles, Project? project = null) - => _completionServiceWithProviders._providerManager.GetTestAccessor().GetProviders(roles, project); + public ImmutableArray GetImportedAndBuiltInProviders(ImmutableHashSet roles) + => _completionServiceWithProviders._providerManager.GetTestAccessor().GetImportedAndBuiltInProviders(roles); - internal async Task GetContextAsync( + public Task> GetProjectProvidersAsync(Project project) + => _completionServiceWithProviders._providerManager.GetTestAccessor().GetProjectProvidersAsync(project); + + public async Task GetContextAsync( CompletionProvider provider, Document document, int position, diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs index eebce9b7f1d25..eb645819fa208 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs @@ -114,7 +114,7 @@ public override Task GetChangeAsync(Document document, Complet private IEmbeddedLanguage GetLanguage(CompletionItem item) { if (_languageProviders.IsDefault) - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); return _languageProviders.Single(lang => lang.CompletionProvider?.Name == item.Properties[EmbeddedProviderName]); } diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs index ce4c99d5a4242..64b6fe0dd2a11 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs @@ -126,7 +126,7 @@ private static bool CanSupportObjectInitializer(ISymbol symbol) return !propertySymbol.Type.IsStructType(); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index 45eef1a97647b..7a75af397548b 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -227,6 +227,8 @@ private TypeImportCompletionCacheEntry CreateCacheWorker( return cacheEntry; } + private static string ConcatNamespace(string? containingNamespace, string name) + => string.IsNullOrEmpty(containingNamespace) ? name : containingNamespace + "." + name; private static void GetCompletionItemsForTopLevelTypeDeclarations( INamespaceSymbol rootNamespaceSymbol, @@ -243,7 +245,7 @@ static void VisitNamespace( CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - containingNamespace = CompletionHelper.ConcatNamespace(containingNamespace, symbol.Name); + containingNamespace = ConcatNamespace(containingNamespace, symbol.Name); foreach (var memberNamespace in symbol.GetNamespaceMembers()) { diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs index 8bf4a98ae6c67..cecf28ddfdaae 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -86,7 +86,7 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke await GetUpToDateCacheEntryAsync(relevantProject, cacheService, cancellationToken).ConfigureAwait(false); foreach (var peReference in GetAllRelevantPeReferences(project)) - await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, cancellationToken).ConfigureAwait(false); + await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, checksum: null, cancellationToken).ConfigureAwait(false); } public async Task<(ImmutableArray symbols, bool isPartialResult)> GetExtensionMethodSymbolsAsync(bool forceCacheCreation, bool hideAdvancedMembers, CancellationToken cancellationToken) @@ -199,7 +199,7 @@ private static ImmutableArray GetAllRelevantPeRefer if (forceCacheCreation) { symbolInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( - _originatingDocument.Project.Solution, peReference, cancellationToken).ConfigureAwait(false); + _originatingDocument.Project.Solution, peReference, checksum: null, cancellationToken).ConfigureAwait(false); } else { diff --git a/src/Features/Core/Portable/Completion/Providers/Snippets/AbstractSnippetCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/Snippets/AbstractSnippetCompletionProvider.cs index 9a45d360fda36..4eeedff9a4c1e 100644 --- a/src/Features/Core/Portable/Completion/Providers/Snippets/AbstractSnippetCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/Snippets/AbstractSnippetCompletionProvider.cs @@ -20,6 +20,8 @@ namespace Microsoft.CodeAnalysis.Completion.Providers.Snippets { internal abstract class AbstractSnippetCompletionProvider : CompletionProvider { + internal override bool IsSnippetProvider => true; + public override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default) { // This retrieves the document without the text used to invoke completion diff --git a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs index 0f1290daf51d2..96f06ca0432d4 100644 --- a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs +++ b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs @@ -101,20 +101,30 @@ public bool Supports(Feature feature) // private bool ParseIfStatementSequence(ReadOnlySpan operations, ArrayBuilder sections, out IOperation? defaultBodyOpt) { - if (operations.Length > 1 && - operations[0] is IConditionalOperation { WhenFalse: null } op && - HasUnreachableEndPoint(op.WhenTrue)) + var current = 0; + while (current < operations.Length && + operations[current] is IConditionalOperation { WhenFalse: null } op && + HasUnreachableEndPoint(op.WhenTrue) && + ParseIfStatement(op, sections, out _)) { - if (!ParseIfStatement(op, sections, out defaultBodyOpt)) - { - return false; - } + current++; + } - if (!ParseIfStatementSequence(operations[1..], sections, out defaultBodyOpt)) + defaultBodyOpt = null; + if (current == 0) + { + // didn't consume a sequence of if-statements with unreachable ends. Check for the last case. + return operations.Length > 0 && ParseIfStatement(operations[0], sections, out defaultBodyOpt); + } + else + { + if (current < operations.Length) { - var nextStatement = operations[1]; - if (nextStatement is IReturnOperation { ReturnedValue: { } } or - IThrowOperation { Exception: { } }) + // consumed a sequence of if-statements with unreachable-ends. If we end with a normal + // if-statement, we're done. Otherwise, we end with whatever last return/throw we see. + var nextStatement = operations[current]; + if (!ParseIfStatement(nextStatement, sections, out defaultBodyOpt) && + nextStatement is IReturnOperation { ReturnedValue: not null } or IThrowOperation { Exception: not null }) { defaultBodyOpt = nextStatement; } @@ -122,14 +132,6 @@ private bool ParseIfStatementSequence(ReadOnlySpan operations, Array return true; } - - if (operations.Length > 0) - { - return ParseIfStatement(operations[0], sections, out defaultBodyOpt); - } - - defaultBodyOpt = null; - return false; } // Tree to parse: diff --git a/src/Features/Core/Portable/Debugging/DebugInformationReaderProvider.cs b/src/Features/Core/Portable/Debugging/DebugInformationReaderProvider.cs index 54d0e69c4bf10..cb00c12155ad5 100644 --- a/src/Features/Core/Portable/Debugging/DebugInformationReaderProvider.cs +++ b/src/Features/Core/Portable/Debugging/DebugInformationReaderProvider.cs @@ -28,13 +28,13 @@ private sealed class DummySymReaderMetadataProvider : ISymReaderMetadataProvider public static readonly DummySymReaderMetadataProvider Instance = new(); public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespaceName, out string typeName, out TypeAttributes attributes) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public bool TryGetTypeReferenceInfo(int typeReferenceToken, out string namespaceName, out string typeName) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); } private sealed class Portable : DebugInformationReaderProvider diff --git a/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs index 49be4d188a8cb..3f35470afd51d 100644 --- a/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs @@ -3,15 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; -using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -22,138 +19,51 @@ namespace Microsoft.CodeAnalysis.DesignerAttribute [ExportWorkspaceService(typeof(IDesignerAttributeDiscoveryService)), Shared] internal sealed partial class DesignerAttributeDiscoveryService : IDesignerAttributeDiscoveryService { - /// - /// Protects mutable state in this type. - /// - private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1); - - /// - /// Keep track of the last information we reported. We will avoid notifying the host if we recompute and these - /// don't change. - /// - private readonly ConcurrentDictionary _documentToLastReportedInformation = new(); - [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public DesignerAttributeDiscoveryService() { } - public async ValueTask ProcessSolutionAsync( - Solution solution, - DocumentId? priorityDocumentId, - IDesignerAttributeDiscoveryService.ICallback callback, - CancellationToken cancellationToken) - { - using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - { - // Remove any documents that are now gone. - foreach (var docId in _documentToLastReportedInformation.Keys) - { - if (!solution.ContainsDocument(docId)) - _documentToLastReportedInformation.TryRemove(docId, out _); - } - - // Handle the priority doc first. - var priorityDocument = solution.GetDocument(priorityDocumentId); - if (priorityDocument != null) - { - await ProcessProjectAsync(priorityDocument.Project, priorityDocument, callback, cancellationToken).ConfigureAwait(false); - - // now scan all the other files from that project in case they were affected by the edited document. - await ProcessProjectAsync(priorityDocument.Project, specificDocument: null, callback, cancellationToken).ConfigureAwait(false); - } - - // Process the rest of the projects in dependency order so that their data is ready when we hit the - // projects that depend on them. - var dependencyGraph = solution.GetProjectDependencyGraph(); - foreach (var projectId in dependencyGraph.GetTopologicallySortedProjects(cancellationToken)) - { - if (projectId != priorityDocumentId?.ProjectId) - await ProcessProjectAsync(solution.GetRequiredProject(projectId), specificDocument: null, callback, cancellationToken).ConfigureAwait(false); - } - } - } - - private async Task ProcessProjectAsync( + public async IAsyncEnumerable ProcessProjectAsync( Project project, - Document? specificDocument, - IDesignerAttributeDiscoveryService.ICallback callback, - CancellationToken cancellationToken) + DocumentId? priorityDocumentId, + [EnumeratorCancellation] CancellationToken cancellationToken) { + // Ignore projects that don't support compilation or don't even have the DesignerCategoryAttribute in it. if (!project.SupportsCompilation) - return; + yield break; - // We need to reanalyze the project whenever it (or any of its dependencies) have - // changed. We need to know about dependencies since if a downstream project adds the - // DesignerCategory attribute to a class, that can affect us when we examine the classes - // in this project. - var projectVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - // Now get all the values that actually changed and notify VS about them. We don't need - // to tell it about the ones that didn't change since that will have no effect on the - // user experience. - var changedData = await ComputeChangedDataAsync( - project, specificDocument, projectVersion, cancellationToken).ConfigureAwait(false); - - // Only bother reporting non-empty information to save an unnecessary RPC. - if (!changedData.IsEmpty) - await callback.ReportDesignerAttributeDataAsync(changedData, cancellationToken).ConfigureAwait(false); - - // Now, keep track of what we've reported to the host so we won't report unchanged files in the future. We - // do this after the report has gone through as we want to make sure that if it cancels for any reason we - // don't hold onto values that may not have made it all the way to the project system. - foreach (var data in changedData) - _documentToLastReportedInformation[data.DocumentId] = (data.Category, projectVersion); - } - - private async Task> ComputeChangedDataAsync( - Project project, Document? specificDocument, VersionStamp projectVersion, CancellationToken cancellationToken) - { var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var designerCategoryType = compilation.DesignerCategoryAttributeType(); + if (designerCategoryType == null) + yield break; - using var _1 = ArrayBuilder>.GetInstance(out var tasks); - foreach (var document in project.Documents) + // If there is a priority doc, then scan that first. + var priorityDocument = priorityDocumentId == null ? null : project.GetDocument(priorityDocumentId); + if (priorityDocument is { FilePath: not null }) { - // If we're only analyzing a specific document, then skip the rest. - if (specificDocument != null && document != specificDocument) - continue; - - // If we don't have a path for this document, we cant proceed with it. - // We need that path to inform the project system which file we're referring to. - if (document.FilePath == null) - continue; + var data = await ComputeDesignerAttributeDataAsync(designerCategoryType, priorityDocument, cancellationToken).ConfigureAwait(false); + if (data != null) + yield return data.Value; + } - // If nothing has changed at the top level between the last time we analyzed this document and now, then - // no need to analyze again. - if (_documentToLastReportedInformation.TryGetValue(document.Id, out var existingInfo) && - existingInfo.projectVersion == projectVersion) - { + // now process the rest of the documents. + using var _ = ArrayBuilder>.GetInstance(out var tasks); + foreach (var document in project.Documents) + { + if (document == priorityDocument || document.FilePath is null) continue; - } tasks.Add(ComputeDesignerAttributeDataAsync(designerCategoryType, document, cancellationToken)); } - using var _2 = ArrayBuilder.GetInstance(tasks.Count, out var results); - - // Avoid unnecessary allocation of result array. - await Task.WhenAll((IEnumerable)tasks).ConfigureAwait(false); - - foreach (var task in tasks) + // Convert the tasks into one final stream we can read all the results from. + await foreach (var dataOpt in tasks.ToImmutable().StreamAsync(cancellationToken).ConfigureAwait(false)) { - var dataOpt = await task.ConfigureAwait(false); - if (dataOpt == null) - continue; - - var data = dataOpt.Value; - _documentToLastReportedInformation.TryGetValue(data.DocumentId, out var existingInfo); - if (existingInfo.category != data.Category) - results.Add(data); + if (dataOpt != null) + yield return dataOpt.Value; } - - return results.ToImmutableAndClear(); } private static async Task ComputeDesignerAttributeDataAsync( @@ -163,18 +73,16 @@ private async Task> ComputeChangedDataAsyn { Contract.ThrowIfNull(document.FilePath); - // We either haven't computed the designer info, or our data was out of date. We need - // So recompute here. Figure out what the current category is, and if that's different - // from what we previously stored. var category = await DesignerAttributeHelpers.ComputeDesignerAttributeCategoryAsync( designerCategoryType, document, cancellationToken).ConfigureAwait(false); - return new DesignerAttributeData - { - Category = category, - DocumentId = document.Id, - FilePath = document.FilePath, - }; + // If there's no category (the common case) don't return anything. The host itself will see no results + // returned and can handle that case (for example, if a type previously had the attribute but doesn't + // any longer). + if (category == null) + return null; + + return new DesignerAttributeData(category, document.Id, document.FilePath); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { diff --git a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeData.cs b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeData.cs index 150e957379fb3..b17014b068654 100644 --- a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeData.cs +++ b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeData.cs @@ -10,24 +10,34 @@ namespace Microsoft.CodeAnalysis.DesignerAttribute /// Serialization typed used to pass information to/from OOP and VS. /// [DataContract] - internal struct DesignerAttributeData + internal readonly struct DesignerAttributeData { /// /// The category specified in a [DesignerCategory("...")] attribute. /// [DataMember(Order = 0)] - public string? Category; + public readonly string? Category; /// /// The document this applies to. /// [DataMember(Order = 1)] - public DocumentId DocumentId; + public readonly DocumentId DocumentId; /// /// Path for this . /// [DataMember(Order = 2)] - public string FilePath; + public readonly string FilePath; + + public DesignerAttributeData(string? category, DocumentId documentId, string filePath) + { + Category = category; + DocumentId = documentId; + FilePath = filePath; + } + + public DesignerAttributeData WithCategory(string? category) + => new(category, DocumentId, FilePath); } } diff --git a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeHelpers.cs b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeHelpers.cs index 038f14f77fc8b..97ffc48cef825 100644 --- a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeHelpers.cs +++ b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeHelpers.cs @@ -54,6 +54,9 @@ internal static class DesignerAttributeHelpers } return null; + + static string? GetArgumentString(TypedConstant argument) + => argument is { IsNull: false, Type.SpecialType: SpecialType.System_String, Value: string stringValue } ? stringValue.Trim() : null; } private static SyntaxNode? FindFirstNonNestedClass( @@ -77,18 +80,5 @@ internal static class DesignerAttributeHelpers return null; } - - private static string? GetArgumentString(TypedConstant argument) - { - if (argument.Type == null || - argument.Type.SpecialType != SpecialType.System_String || - argument.IsNull || - argument.Value is not string stringValue) - { - return null; - } - - return stringValue.Trim(); - } } } diff --git a/src/Features/Core/Portable/DesignerAttribute/IDesignerAttributeDiscoveryService.cs b/src/Features/Core/Portable/DesignerAttribute/IDesignerAttributeDiscoveryService.cs index c3bc3fbf0af0d..c98d3fd5c4998 100644 --- a/src/Features/Core/Portable/DesignerAttribute/IDesignerAttributeDiscoveryService.cs +++ b/src/Features/Core/Portable/DesignerAttribute/IDesignerAttributeDiscoveryService.cs @@ -2,21 +2,14 @@ // 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.Collections.Generic; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.DesignerAttribute { - internal partial interface IDesignerAttributeDiscoveryService : IWorkspaceService + internal interface IDesignerAttributeDiscoveryService : IWorkspaceService { - public interface ICallback - { - ValueTask ReportDesignerAttributeDataAsync(ImmutableArray data, CancellationToken cancellationToken); - } - - ValueTask ProcessSolutionAsync(Solution solution, DocumentId? priorityDocumentId, ICallback callback, CancellationToken cancellationToken); + IAsyncEnumerable ProcessProjectAsync(Project project, DocumentId? priorityDocumentId, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs b/src/Features/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs index 4716273c30a92..15c8d86e9fda3 100644 --- a/src/Features/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs +++ b/src/Features/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs @@ -2,13 +2,8 @@ // 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.Collections.Generic; using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Remote; namespace Microsoft.CodeAnalysis.DesignerAttribute { @@ -18,27 +13,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttribute /// internal interface IRemoteDesignerAttributeDiscoveryService { - internal interface ICallback - { - ValueTask ReportDesignerAttributeDataAsync(RemoteServiceCallbackId callbackId, ImmutableArray data, CancellationToken cancellationToken); - } - - ValueTask DiscoverDesignerAttributesAsync(RemoteServiceCallbackId callbackId, Checksum solutionChecksum, DocumentId? priorityDocument, CancellationToken cancellationToken); - } - - [ExportRemoteServiceCallbackDispatcher(typeof(IRemoteDesignerAttributeDiscoveryService)), Shared] - internal sealed class RemoteDesignerAttributeDiscoveryCallbackDispatcher : RemoteServiceCallbackDispatcher, IRemoteDesignerAttributeDiscoveryService.ICallback - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RemoteDesignerAttributeDiscoveryCallbackDispatcher() - { - } - - private new IDesignerAttributeDiscoveryService.ICallback GetCallback(RemoteServiceCallbackId callbackId) - => (IDesignerAttributeDiscoveryService.ICallback)base.GetCallback(callbackId); - - public ValueTask ReportDesignerAttributeDataAsync(RemoteServiceCallbackId callbackId, ImmutableArray data, CancellationToken cancellationToken) - => GetCallback(callbackId).ReportDesignerAttributeDataAsync(data, cancellationToken); + IAsyncEnumerable DiscoverDesignerAttributesAsync(Checksum solutionChecksum, ProjectId project, DocumentId? priorityDocument, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/Diagnostics/BuildOnlyDiagnosticsService.cs b/src/Features/Core/Portable/Diagnostics/BuildOnlyDiagnosticsService.cs new file mode 100644 index 0000000000000..fae16dfdbf017 --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/BuildOnlyDiagnosticsService.cs @@ -0,0 +1,147 @@ +// 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 Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + [ExportWorkspaceServiceFactory(typeof(IBuildOnlyDiagnosticsService), ServiceLayer.Default), Shared] + internal sealed class BuildOnlyDiagnosticsServiceFactory : IWorkspaceServiceFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public BuildOnlyDiagnosticsServiceFactory() + { + } + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => new BuildOnlyDiagnosticsService(workspaceServices.Workspace); + + private sealed class BuildOnlyDiagnosticsService : IBuildOnlyDiagnosticsService + { + private readonly object _gate = new(); + private readonly Dictionary> _documentDiagnostics = new(); + private readonly Dictionary> _projectDiagnostics = new(); + + public BuildOnlyDiagnosticsService(Workspace workspace) + { + workspace.WorkspaceChanged += OnWorkspaceChanged; + } + + private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) + { + switch (e.Kind) + { + case WorkspaceChangeKind.SolutionAdded: + case WorkspaceChangeKind.SolutionCleared: + case WorkspaceChangeKind.SolutionReloaded: + case WorkspaceChangeKind.SolutionRemoved: + ClearAllDiagnostics(); + break; + + case WorkspaceChangeKind.ProjectReloaded: + case WorkspaceChangeKind.ProjectRemoved: + ClearDiagnostics(e.OldSolution.GetProject(e.ProjectId)); + break; + + case WorkspaceChangeKind.DocumentRemoved: + case WorkspaceChangeKind.DocumentReloaded: + case WorkspaceChangeKind.AdditionalDocumentRemoved: + case WorkspaceChangeKind.AdditionalDocumentReloaded: + case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: + case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: + ClearDiagnostics(e.DocumentId); + break; + } + } + + public void AddBuildOnlyDiagnostics(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray diagnostics) + { + lock (_gate) + { + if (documentId != null) + { + _documentDiagnostics[documentId] = diagnostics; + } + else if (projectId != null) + { + _projectDiagnostics[projectId] = diagnostics; + } + } + } + + private void ClearAllDiagnostics() + { + lock (_gate) + { + _documentDiagnostics.Clear(); + _projectDiagnostics.Clear(); + } + } + + private void ClearDiagnostics(DocumentId? documentId) + { + if (documentId == null) + return; + + lock (_gate) + { + _documentDiagnostics.Remove(documentId); + } + } + + private void ClearDiagnostics(Project? project) + { + if (project == null) + return; + + lock (_gate) + { + _projectDiagnostics.Remove(project.Id); + foreach (var documentId in project.DocumentIds) + _documentDiagnostics.Remove(documentId); + } + } + + public void ClearBuildOnlyDiagnostics(Solution solution, ProjectId? projectId, DocumentId? documentId) + { + if (documentId != null) + ClearDiagnostics(documentId); + else + ClearDiagnostics(solution.GetProject(projectId)); + } + + public ImmutableArray GetBuildOnlyDiagnostics(DocumentId documentId) + { + lock (_gate) + { + if (_documentDiagnostics.TryGetValue(documentId, out var diagnostics)) + { + return diagnostics; + } + + return ImmutableArray.Empty; + } + } + + public ImmutableArray GetBuildOnlyDiagnostics(ProjectId projectId) + { + lock (_gate) + { + if (_projectDiagnostics.TryGetValue(projectId, out var diagnostics)) + { + return diagnostics; + } + + return ImmutableArray.Empty; + } + } + } + } +} diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs index 7d7ced96ec331..1681701174106 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs @@ -53,7 +53,7 @@ private static VersionStamp GetAnalyzerVersion(string path) } public static string GetAnalyzerAssemblyName(this DiagnosticAnalyzer analyzer) - => analyzer.GetType().Assembly.GetName().Name ?? throw ExceptionUtilities.Unreachable; + => analyzer.GetType().Assembly.GetName().Name ?? throw ExceptionUtilities.Unreachable(); public static void AppendAnalyzerMap(this Dictionary analyzerMap, IEnumerable analyzers) { diff --git a/src/Features/Core/Portable/Diagnostics/IBuildOnlyDiagnosticsService.cs b/src/Features/Core/Portable/Diagnostics/IBuildOnlyDiagnosticsService.cs new file mode 100644 index 0000000000000..52a34b01326db --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/IBuildOnlyDiagnosticsService.cs @@ -0,0 +1,24 @@ +// 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.Collections.Immutable; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + /// + /// Service to keep track of build-only diagnostics reported from explicit Build/Rebuild commands. + /// Note that this service only keeps track of those diagnostics that can never be reported from live analysis. + /// + internal interface IBuildOnlyDiagnosticsService : IWorkspaceService + { + void AddBuildOnlyDiagnostics(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray diagnostics); + + void ClearBuildOnlyDiagnostics(Solution solution, ProjectId? projectId, DocumentId? documentId); + + ImmutableArray GetBuildOnlyDiagnostics(DocumentId documentId); + + ImmutableArray GetBuildOnlyDiagnostics(ProjectId projectId); + } +} diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 8117cfd3adab4..0976584cc372e 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -4552,7 +4552,7 @@ private static SyntaxNode GetRudeEditDiagnosticNode(ISymbol symbol, Cancellation container = container.ContainingSymbol; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static SyntaxNode GetDeleteRudeEditDiagnosticNode(ISymbol oldSymbol, Compilation newCompilation, CancellationToken cancellationToken) @@ -4570,7 +4570,7 @@ private static SyntaxNode GetDeleteRudeEditDiagnosticNode(ISymbol oldSymbol, Com oldContainer = oldContainer.ContainingSymbol; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } #region Type Layout Update Validation diff --git a/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs b/src/Features/Core/Portable/EditAndContinue/CommittedSolution.cs index 2e461af6354d0..66ef46bdb0d75 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; @@ -130,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 @@ -229,24 +219,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 +239,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 +253,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. @@ -288,15 +271,16 @@ 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)); } + var matchingSourceText = maybeMatchingSourceText.Value; if (matchingSourceText != null) { if (committedDocument != null && sourceText.ContentEquals(matchingSourceText)) @@ -323,9 +307,48 @@ 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, _debuggingSession.SourceTextProvider, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) : default; + + return (maybeMatchingSourceText, maybePdbHasDocument); + } + + private static async ValueTask> TryGetMatchingSourceTextAsync( + SourceText sourceText, string filePath, Document? currentDocument, IPdbMatchingSourceTextProvider sourceTextProvider, 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; + } + } + + 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 => @@ -364,8 +387,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, sourceTextProvider, requiredChecksum, checksumAlgorithm, cancellationToken).ConfigureAwait(false) is { HasValue: true, Value: not null }) { return documentState.Id; } @@ -411,14 +434,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 +447,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 +519,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/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index 6089bddeb9948..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( @@ -759,7 +759,7 @@ public async ValueTask>> GetB } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -836,11 +836,12 @@ public async ValueTask> GetAdjustedActiveSta } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } - 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 { @@ -1036,13 +1038,14 @@ await GetChangedDocumentContainingUnmappedActiveStatementAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } // 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/EditAndContinueDocumentAnalysesCache.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs index 48a498eac5783..811bb90b579c8 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs @@ -55,7 +55,7 @@ public async ValueTask> GetDocumentAnaly } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -96,7 +96,7 @@ public async ValueTask GetDocumentAnalysisAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -198,7 +198,7 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }, cacheResult: true); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 1b30385d56959..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) { @@ -145,7 +136,7 @@ public async ValueTask StartDebuggingSessionAsync( } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 324ac9a7695cd..5f280b903b260 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -658,7 +658,7 @@ internal static async ValueTask GetProjectChangesAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1090,7 +1090,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } 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/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index a399709766c12..fffd749016755 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -276,7 +276,7 @@ private bool IsAttributeArgumentWithMatchingStringSyntaxAttribute( return HasMatchingStringSyntaxAttribute(fieldOrProperty, out identifier); // Otherwise, see if it's a normal named/position argument to the attribute. - var parameter = Info.SemanticFacts.FindParameterForAttributeArgument(semanticModel, argument, allowUncertainCandidates: true, cancellationToken); + var parameter = Info.SemanticFacts.FindParameterForAttributeArgument(semanticModel, argument, allowUncertainCandidates: true, allowParams: true, cancellationToken); return HasMatchingStringSyntaxAttribute(parameter, out identifier); } @@ -290,7 +290,7 @@ private bool IsArgumentWithMatchingStringSyntaxAttribute( if (fieldOrProperty != null) return HasMatchingStringSyntaxAttribute(fieldOrProperty, out identifier); - var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argument, allowUncertainCandidates: true, cancellationToken); + var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argument, allowUncertainCandidates: true, allowParams: true, cancellationToken); return HasMatchingStringSyntaxAttribute(parameter, out identifier); } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonLanguageDetector.cs index dd60351bf5a0c..fddc8de441756 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/JsonLanguageDetector.cs @@ -212,7 +212,7 @@ protected override bool TryGetOptions( private bool IsArgumentToSuitableParameter( SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) { - var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates: true, cancellationToken); + var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates: true, allowParams: true, cancellationToken); return parameter?.Name == JsonParameterName; } } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexLanguageDetector.cs index 900a6b5d829e0..cf6a94487753b 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexLanguageDetector.cs @@ -192,7 +192,7 @@ private bool AnalyzeStringLiteral( { options = default; - var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates: true, cancellationToken); + var parameter = Info.SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates: true, allowParams: true, cancellationToken); if (parameter?.Name != _patternName) { return false; diff --git a/src/Features/Core/Portable/Emit/CompilationOutputFilesWithImplicitPdbPath.cs b/src/Features/Core/Portable/Emit/CompilationOutputFilesWithImplicitPdbPath.cs index 93f33bcea44e6..54689b71b3d27 100644 --- a/src/Features/Core/Portable/Emit/CompilationOutputFilesWithImplicitPdbPath.cs +++ b/src/Features/Core/Portable/Emit/CompilationOutputFilesWithImplicitPdbPath.cs @@ -39,7 +39,7 @@ public CompilationOutputFilesWithImplicitPdbPath(string? assemblyFilePath = null // Not gonna be called since we override OpenPdb. protected override Stream OpenPdbStream() - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override DebugInformationReaderProvider? OpenPdb() { diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerImplementation.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerImplementation.cs new file mode 100644 index 0000000000000..49aab6152f97a --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerImplementation.cs @@ -0,0 +1,44 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + internal interface INewUnitTestingIncrementalAnalyzerImplementation + { +#if false // Not used in unit testing crawling + // Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); + // Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); + // Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); + // Task DocumentResetAsync(Document document, CancellationToken cancellationToken); + // void RemoveProject(ProjectId projectId); + + // [Obsolete] + // bool NeedsReanalysisOnOptionChanged(object sender, UnitTestingOptionChangedEventArgsWrapper e); + + Task AnalyzeSyntaxAsync(Document document, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken); +#endif + + Task AnalyzeDocumentAsync( + Document document, +#if false // Not used in unit testing crawling + SyntaxNode bodyOpt, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken); + + Task AnalyzeProjectAsync( + Project project, +#if false // Not used in unit testing crawling + bool semanticsChanged, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken); + + void RemoveDocument(DocumentId documentId); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerProviderImplementation.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerProviderImplementation.cs new file mode 100644 index 0000000000000..88b311e56876a --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/INewUnitTestingIncrementalAnalyzerProviderImplementation.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + internal interface INewUnitTestingIncrementalAnalyzerProviderImplementation + { + INewUnitTestingIncrementalAnalyzerImplementation CreateIncrementalAnalyzer(); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzer.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..f3e352065b04e --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzer.cs @@ -0,0 +1,116 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; + +internal sealed partial class NewUnitTestingIncrementalAnalyzerProvider +{ + private sealed class NewUnitTestingIncrementalAnalyzer : IUnitTestingIncrementalAnalyzer + { + private readonly INewUnitTestingIncrementalAnalyzerImplementation _implementation; + + public NewUnitTestingIncrementalAnalyzer(INewUnitTestingIncrementalAnalyzerImplementation implementation) + => _implementation = implementation; + + public Task AnalyzeDocumentAsync( + Document document, +#if false // Not used in unit testing crawling + SyntaxNode bodyOpt, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken) + { + return _implementation.AnalyzeDocumentAsync( + document, +#if false // Not used in unit testing crawling + bodyOpt, +#endif + reasons, + cancellationToken); + } + + public Task AnalyzeProjectAsync( + Project project, +#if false // Not used in unit testing crawling + bool semanticsChanged, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken) + { + return _implementation.AnalyzeProjectAsync( + project, +#if false // Not used in unit testing crawling + semanticsChanged, +#endif + reasons, + cancellationToken); + } + +#if false // Not used in unit testing crawling + public Task AnalyzeSyntaxAsync(Document document, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) + => Task.CompletedTask; +#endif + + public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) + { + _implementation.RemoveDocument(documentId); + return Task.CompletedTask; + } + +#if false // Not used in unit testing crawling + public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + => Task.CompletedTask; + + public void LogAnalyzerCountSummary() + { + } + + /// + /// Order all incremental analyzers below DiagnosticIncrementalAnalyzer + /// + public int Priority => 1; + + // Unit testing incremental analyzer only supports full solution analysis scope. + // In future, we should add a separate option to allow users to configure background analysis scope for unit testing. + public static BackgroundAnalysisScope GetBackgroundAnalysisScope(OptionSet _) => BackgroundAnalysisScope.FullSolution; + + public void Shutdown() + { + } +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs new file mode 100644 index 0000000000000..da51d3ecdebfc --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs @@ -0,0 +1,64 @@ +// 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.ExternalAccess.UnitTesting.SolutionCrawler; +using Microsoft.CodeAnalysis.Host; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; + +internal sealed partial class NewUnitTestingIncrementalAnalyzerProvider : IUnitTestingIncrementalAnalyzerProvider +{ + private readonly string? _workspaceKind; + private readonly SolutionServices _services; + private readonly INewUnitTestingIncrementalAnalyzerProviderImplementation _incrementalAnalyzerProvider; + + private IUnitTestingIncrementalAnalyzer? _lazyAnalyzer; + + private NewUnitTestingIncrementalAnalyzerProvider( + string? workspaceKind, + SolutionServices services, + INewUnitTestingIncrementalAnalyzerProviderImplementation incrementalAnalyzerProvider) + { + _workspaceKind = workspaceKind; + _services = services; + _incrementalAnalyzerProvider = incrementalAnalyzerProvider; + } + + // NOTE: We're currently expecting the analyzer to be singleton, so that + // analyzers returned when calling this method twice would pass a reference equality check. + // One instance should be created by SolutionCrawler, another one by us, when calling the + // UnitTestingSolutionCrawlerServiceAccessor.Reanalyze method. + public IUnitTestingIncrementalAnalyzer CreateIncrementalAnalyzer() + => _lazyAnalyzer ??= new NewUnitTestingIncrementalAnalyzer(_incrementalAnalyzerProvider.CreateIncrementalAnalyzer()); + + public void Reanalyze() + { + var solutionCrawlerService = _services.GetService(); + solutionCrawlerService?.Reanalyze( + _workspaceKind, _services, this.CreateIncrementalAnalyzer(), projectIds: null, documentIds: null, highPriority: false); + } + + public static NewUnitTestingIncrementalAnalyzerProvider? TryRegister(string? workspaceKind, SolutionServices services, string analyzerName, INewUnitTestingIncrementalAnalyzerProviderImplementation provider) + { + Contract.ThrowIfNull(workspaceKind); + var solutionCrawlerRegistrationService = services.GetService(); + if (solutionCrawlerRegistrationService == null) + { + return null; + } + + var analyzerProvider = new NewUnitTestingIncrementalAnalyzerProvider(workspaceKind, services, provider); + + var metadata = new UnitTestingIncrementalAnalyzerProviderMetadata( + analyzerName, +#if false // Not used in unit testing crawling + highPriorityForActiveFile: false, +#endif + new[] { workspaceKind }); + + solutionCrawlerRegistrationService.AddAnalyzerProvider(analyzerProvider, metadata); + return analyzerProvider; + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingDocumentSpan.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingDocumentSpan.cs new file mode 100644 index 0000000000000..ae44568622049 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingDocumentSpan.cs @@ -0,0 +1,39 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.EditAndContinue.Contracts; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + internal readonly struct UnitTestingDocumentSpan + { + internal UnitTestingDocumentSpan(DocumentSpan sourceSpan, FileLinePositionSpan span) + { + DocumentSpan = sourceSpan; + Span = span; + } + + /// + /// The raw and that the symbol is located at. + /// + public DocumentSpan DocumentSpan { get; } + + /// + /// The line and character the symbol is located. If this is a mapped location (e.g. affected by a #line + /// directive), this will be the final location the symbol was mapped to. + /// + public FileLinePositionSpan Span { get; } + + public async Task NavigateToAsync(UnitTestingNavigationOptions options, CancellationToken cancellationToken) + { + var location = await this.DocumentSpan.GetNavigableLocationAsync(cancellationToken).ConfigureAwait(false); + if (location != null) + await location.NavigateToAsync(new NavigationOptions(options.PreferProvisionalTab, options.ActivateTab), cancellationToken).ConfigureAwait(false); + } + } +} 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/UnitTesting/API/UnitTestingNavigationOptions.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingNavigationOptions.cs new file mode 100644 index 0000000000000..973fa2c8bec09 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingNavigationOptions.cs @@ -0,0 +1,16 @@ +// 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. + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + internal readonly record struct UnitTestingNavigationOptions( + bool PreferProvisionalTab = false, + bool ActivateTab = true) + { + public UnitTestingNavigationOptions() + : this(PreferProvisionalTab: false) + { + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchHelpers.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchHelpers.cs new file mode 100644 index 0000000000000..e810ec7fc3f31 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchHelpers.cs @@ -0,0 +1,185 @@ +// 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.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + internal static class UnitTestingSearchHelpers + { + private static readonly char[] s_splitCharacters = { '.', '+' }; + + public static async Task> GetSourceLocationsAsync( + Project project, UnitTestingSearchQuery query, CancellationToken cancellationToken) + { + if (!project.SupportsCompilation) + return ImmutableArray.Empty; + + var client = await RemoteHostClient.TryGetClientAsync(project.Solution.Services, cancellationToken).ConfigureAwait(false); + + if (client != null) + { + var locations = await client.TryInvokeAsync>( + project, + (service, solutionChecksum, cancellationToken) => service.GetSourceLocationsAsync(solutionChecksum, project.Id, query, cancellationToken), + cancellationToken).ConfigureAwait(false); + if (!locations.HasValue) + return ImmutableArray.Empty; + + using var _ = ArrayBuilder.GetInstance(out var result); + foreach (var location in locations.Value) + result.AddIfNotNull(await location.TryRehydrateAsync(project.Solution, cancellationToken).ConfigureAwait(false)); + + return result.ToImmutable(); + } + + return await GetSourceLocationsInProcessAsync(project, query, cancellationToken).ConfigureAwait(false); + } + + private static (string containerName, string symbolName, int symbolArity) ExtractQueryData(UnitTestingSearchQuery query) + { + if (query.MethodName == null) + { + // if we don't have a method name, then the fully qualified type name needs to be broken into two parts: + // 1. the name of the type symbol we're looking for (the last name portion of the qualified name) + // 2. the container of the type symbol we're looking for (all but the last name portion). + var fullyQualifiedTypeName = query.FullyQualifiedTypeName; + var lastPlus = fullyQualifiedTypeName.LastIndexOf('+'); + var lastDot = fullyQualifiedTypeName.LastIndexOf('.'); + + var (container, type) = + lastPlus >= 0 ? (fullyQualifiedTypeName[..lastPlus], fullyQualifiedTypeName[(lastPlus + 1)..]) : + lastDot >= 0 ? (fullyQualifiedTypeName[..lastDot], fullyQualifiedTypeName[(lastDot + 1)..]) : + ("", fullyQualifiedTypeName); + + GetNameAndArity(type, out type, out var typeArity); + + return (ConvertFromMetadataTypeName(container), type, typeArity); + } + else + { + // If we have a method name, then that's the name we'll search in the index for. The fully qualified + // type name is what we'll use to check the container of any methods we find. + return (ConvertFromMetadataTypeName(query.FullyQualifiedTypeName), query.MethodName, query.MethodArity); + } + } + + /// + /// Converts from a metadata-name into the internal simple dotted name we store in our index as the container + /// for a symbol. In the future, we could consider storing the fully-qualified-metadata-name in our index as + /// it's trivial to compute it as we're walking down the syntax tree. + /// + private static string ConvertFromMetadataTypeName(string fullyQualifiedTypeName) + { + var pieces = fullyQualifiedTypeName.Split(s_splitCharacters); + using var _ = PooledStringBuilder.GetInstance(out var result); + + foreach (var piece in pieces) + { + GetNameAndArity(piece, out var pieceWithoutArity, out var _); + if (result.Length > 0) + result.Append('.'); + + result.Append(pieceWithoutArity); + } + + return result.ToString(); + } + + private static void GetNameAndArity(string typeName, out string typeNameWithoutArity, out int typeArity) + { + var backtickIndex = typeName.LastIndexOf('`'); + if (backtickIndex < 0) + { + typeNameWithoutArity = typeName; + typeArity = 0; + } + else + { + typeNameWithoutArity = typeName[0..backtickIndex]; + int.TryParse(typeName[(backtickIndex + 1)..], NumberStyles.None, CultureInfo.InvariantCulture, out typeArity); + } + } + + private static async Task> GetSourceLocationsInProcessAsync( + Project project, + UnitTestingSearchQuery query, + CancellationToken cancellationToken) + { + var (container, symbolName, symbolArity) = ExtractQueryData(query); + var syntaxFacts = project.GetRequiredLanguageService(); + var comparer = syntaxFacts.StringComparer; + + var tasks = project.Documents.Select(d => GetSourceLocationsInProcessAsync(d, comparer, container, symbolName, symbolArity, query, cancellationToken)); + var result = await Task.WhenAll(tasks).ConfigureAwait(false); + return result.SelectMany(r => r).ToImmutableArray(); + } + + private static async Task> GetSourceLocationsInProcessAsync( + Document document, + StringComparer comparer, + string container, + string symbolName, + int symbolArity, + UnitTestingSearchQuery query, + CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + SyntaxTree? tree = null; + + // Walk each of the top-level-index infos we've got for this tree. + var index = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); + foreach (var info in index.DeclaredSymbolInfos) + { + // Fast checks first to see if this looks like a candidate. + + // In non-strict mode, allow the type-parameter count to be mismatched. + if (query.Strict && info.TypeParameterCount != symbolArity) + continue; + + if (query.MethodName != null) + { + // We're searching for unit test methods. Those must always have an attribute on them of some sort. + if (!info.HasAttributes) + continue; + + // Has to actually be a method. + if (info.Kind is not (DeclaredSymbolInfoKind.Method or DeclaredSymbolInfoKind.ExtensionMethod)) + continue; + + // In non-strict mode, allow the parameter count to be mismatched. + if (query.Strict && info.ParameterCount != query.MethodParameterCount) + continue; + } + + // Looking promising so far. Check that the names matches what the caller needs. + if (!comparer.Equals(info.Name, symbolName)) + continue; + + if (!comparer.Equals(info.FullyQualifiedContainerName, container)) + continue; + + // Unit testing needs to know the final span a location may be mapped to (e.g. with `#line` taken + // into consideration). So map that information here for them. + tree ??= await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var mappedSpan = tree.GetMappedLineSpan(info.Span, cancellationToken); + + result.Add(new UnitTestingDocumentSpan(new DocumentSpan(document, info.Span), mappedSpan)); + } + + return result.ToImmutable(); + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchQuery.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchQuery.cs new file mode 100644 index 0000000000000..52987b4b81ddd --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingSearchQuery.cs @@ -0,0 +1,62 @@ +// 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.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api +{ + [DataContract] + internal sealed class UnitTestingSearchQuery + { + /// + /// Fully qualified metadata name for the type being searched for, or the containing type of the method being + /// searched for (if is provided). Should follow .Net metadata naming, e.g. ` + /// for arity and + for nested types. + /// + [DataMember(Order = 0)] + public readonly string FullyQualifiedTypeName; + + /// + /// Optional name of method within being searched for. Should not include arity. + /// + [DataMember(Order = 1)] + public readonly string? MethodName; + + /// + /// Arity of the method being searched for. Only valid if is non-null. + /// + [DataMember(Order = 2)] + public readonly int MethodArity; + + /// + /// Parameter count of the method being searched for. Only valid if is non-null. + /// + [DataMember(Order = 3)] + public readonly int MethodParameterCount; + + /// + /// Whether or not this is a strict search or not. Strict searches require matching arity and parameter counts, + /// while non-strict searches do not. Non-strict searches are useful in cases where the initial searching data + /// may not be produced in a well formed fashion (for example, some legacy test providers that do not follow: + /// https://github.com/microsoft/vstest-docs/blob/main/RFCs/0017-Managed-TestCase-Properties.md). + /// + [DataMember(Order = 4)] + public readonly bool Strict; + + public static UnitTestingSearchQuery ForType(string fullyQualifiedTypeName, bool strict = true) + => new(fullyQualifiedTypeName, methodName: null, methodArity: 0, methodParameterCount: 0, strict); + + public static UnitTestingSearchQuery ForMethod(string fullyQualifiedTypeName, string methodName, int methodArity, int methodParameterCount, bool strict = true) + => new(fullyQualifiedTypeName, methodName, methodArity, methodParameterCount, strict); + + private UnitTestingSearchQuery(string fullyQualifiedTypeName, string? methodName, int methodArity, int methodParameterCount, bool strict) + { + FullyQualifiedTypeName = fullyQualifiedTypeName; + MethodName = methodName; + MethodArity = methodArity; + MethodParameterCount = methodParameterCount; + Strict = strict; + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/IRemoteUnitTestingSearchService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/IRemoteUnitTestingSearchService.cs new file mode 100644 index 0000000000000..929185efe09c9 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/IRemoteUnitTestingSearchService.cs @@ -0,0 +1,42 @@ +// 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.Collections.Immutable; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting +{ + internal interface IRemoteUnitTestingSearchService + { + ValueTask> GetSourceLocationsAsync( + Checksum solutionChecksum, ProjectId projectId, UnitTestingSearchQuery query, CancellationToken cancellationToken); + } + + [DataContract] + internal readonly struct UnitTestingSourceLocation + { + [DataMember(Order = 0)] + public readonly DocumentIdSpan DocumentIdSpan; + [DataMember(Order = 1)] + public readonly FileLinePositionSpan Span; + + public UnitTestingSourceLocation(DocumentIdSpan documentIdSpan, FileLinePositionSpan span) + { + DocumentIdSpan = documentIdSpan; + Span = span; + } + + public async Task TryRehydrateAsync(Solution solution, CancellationToken cancellationToken) + { + var documentSpan = await DocumentIdSpan.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (documentSpan == null) + return null; + + return new UnitTestingDocumentSpan(documentSpan.Value, Span); + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/LegacySolutionEvents/UnitTestingLegacySolutionEventsListener.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/LegacySolutionEvents/UnitTestingLegacySolutionEventsListener.cs new file mode 100644 index 0000000000000..5cdaa0a1f0657 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/LegacySolutionEvents/UnitTestingLegacySolutionEventsListener.cs @@ -0,0 +1,71 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LegacySolutionEvents; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.LegacySolutionEvents +{ + /// + /// Retrieves stream of workspace events and forwards them to the dedicated solution crawler instance that exists + /// for unit testing. + /// + [Export(typeof(ILegacySolutionEventsListener)), Shared] + internal class UnitTestingLegacySolutionEventsListener : ILegacySolutionEventsListener + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public UnitTestingLegacySolutionEventsListener() + { + } + + private static IUnitTestingWorkCoordinator? GetCoordinator(Solution solution) + { + var service = solution.Services.GetService(); + if (service == null) + return null; + + return service.Register(solution); + } + + public bool ShouldReportChanges(SolutionServices services) + { + var service = services.GetService(); + if (service == null) + return false; + + return service.HasRegisteredAnalyzerProviders; + } + + public ValueTask OnWorkspaceChangedAsync(WorkspaceChangeEventArgs args, CancellationToken cancellationToken) + { + var coordinator = GetCoordinator(args.NewSolution); + coordinator?.OnWorkspaceChanged(args); + return ValueTaskFactory.CompletedTask; + } + +#if false // Not used in unit testing crawling + public ValueTask OnTextDocumentOpenedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken) + { + var coordinator = GetCoordinator(args.Document.Project.Solution); + coordinator?.OnTextDocumentOpened(args); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask OnTextDocumentClosedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken) + { + var coordinator = GetCoordinator(args.Document.Project.Solution); + coordinator?.OnTextDocumentClosed(args); + return ValueTaskFactory.CompletedTask; + } +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs new file mode 100644 index 0000000000000..4140fa18f74bd --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs @@ -0,0 +1,182 @@ +// 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.Threading; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal abstract class AbstractUnitTestingDocumentDifferenceService : IUnitTestingDocumentDifferenceService + { + public UnitTestingDocumentDifferenceResult? GetDifference(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + { + try + { + var syntaxFactsService = newDocument.Project.Services.GetService(); + if (syntaxFactsService == null) + { + // somehow, we can't get the service. without it, there is nothing we can do. + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.DocumentChanged); + } + // this is based on the implementation detail where opened documents use strong references + // to tree and text rather than recoverable versions. + if (!oldDocument.TryGetText(out var oldText) || + !newDocument.TryGetText(out var newText)) + { + // no cheap way to determine top level changes. assumes top level has changed + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.DocumentChanged); + } + // quick check whether two tree versions are same + if (oldDocument.TryGetSyntaxVersion(out var oldVersion) && + newDocument.TryGetSyntaxVersion(out var newVersion) && + oldVersion.Equals(newVersion)) + { + // nothing has changed. don't do anything. + // this could happen if a document is opened/closed without any buffer change + return null; + } + + var range = newText.GetEncompassingTextChangeRange(oldText); + if (range == default) + { + // nothing has changed. don't do anything + return null; + } + +#if false // Not used in unit testing crawling + var incrementalParsingCandidate = range.NewLength != newText.Length; + // see whether we can get it without explicit parsing + if (!oldDocument.TryGetSyntaxRoot(out var oldRoot) || + !newDocument.TryGetSyntaxRoot(out var newRoot)) + { + if (!incrementalParsingCandidate) + { + // no cheap way to determine top level changes. assumes top level has changed + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.DocumentChanged); + } + + // explicitly parse them + oldRoot = await oldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + Contract.ThrowIfNull(oldRoot); + Contract.ThrowIfNull(newRoot); + } + + // at this point, we must have these version already calculated + if (!oldDocument.TryGetTopLevelChangeTextVersion(out var oldTopLevelChangeVersion) || + !newDocument.TryGetTopLevelChangeTextVersion(out var newTopLevelChangeVersion)) + { + throw ExceptionUtilities.Unreachable; + } + + // quicker common case + if (incrementalParsingCandidate) + { + if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) + { + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.SyntaxChanged, GetChangedMember(syntaxFactsService, oldRoot, newRoot, range)); + } + + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.DocumentChanged, GetBestGuessChangedMember(syntaxFactsService, oldRoot, newRoot, range)); + } + + if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) + { + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.SyntaxChanged); + } +#endif + + return new UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons.DocumentChanged); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + } + +#if false // Not used in unit testing crawling + private static SyntaxNode? GetChangedMember( + ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) + { + // if either old or new tree contains skipped text, re-analyze whole document + if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) + { + return null; + } + + var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); + var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); + + // reached the top (compilation unit) + if (oldMember == null || newMember == null) + { + return null; + } + + // member doesn't contain the change + if (!syntaxFactsService.ContainsInMemberBody(oldMember, range.Span)) + { + return null; + } + + // member signature has changed + if (!oldMember.IsEquivalentTo(newMember, topLevel: true)) + { + return null; + } + + // looks like inside of the body has changed + return newMember; + } + + private static SyntaxNode? GetBestGuessChangedMember( + ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) + { + // if either old or new tree contains skipped text, re-analyze whole document + if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) + { + return null; + } + + // there was top level changes, so we can't use equivalent to see whether two members are same. + // so, we use some simple text based heuristic to find a member that has changed. + // + // if we have a differ that do diff on member level or a way to track member between incremental parsing, then + // that would be preferable. but currently we don't have such thing. + + // get top level elements at the position where change has happened + var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); + var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); + + // reached the top (compilation unit) + if (oldMember == null || newMember == null) + { + return null; + } + + // if old member was empty, just use new member + if (oldMember.Span.IsEmpty) + { + return newMember; + } + + // looks like change doesn't belong to existing member + if (!oldMember.Span.Contains(range.Span)) + { + return null; + } + + // change happened inside of the old member, check whether new member seems just delta of that change + var lengthDelta = range.NewLength - range.Span.Length; + + return (oldMember.Span.Length + lengthDelta) == newMember.Span.Length ? newMember : null; + } +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/DefaultUnitTestingDocumentTrackingService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/DefaultUnitTestingDocumentTrackingService.cs new file mode 100644 index 0000000000000..e7b7f738b493c --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/DefaultUnitTestingDocumentTrackingService.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 Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + [ExportWorkspaceService(typeof(IUnitTestingDocumentTrackingService), ServiceLayer.Default)] + [Shared] + internal sealed class DefaultUnitTestingDocumentTrackingService : IUnitTestingDocumentTrackingService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DefaultUnitTestingDocumentTrackingService() + { + } + + public bool SupportsDocumentTracking => false; + +#if false // Not used in unit testing crawling + public event EventHandler ActiveDocumentChanged { add { } remove { } } +#endif + + public event EventHandler NonRoslynBufferTextChanged { add { } remove { } } + + public ImmutableArray GetVisibleDocuments() + => ImmutableArray.Empty; + + public DocumentId? TryGetActiveDocument() + => null; + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs new file mode 100644 index 0000000000000..4c2e88a2fa20c --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs @@ -0,0 +1,40 @@ +// 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.Composition; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class)] + internal class ExportUnitTestingIncrementalAnalyzerProviderAttribute : ExportAttribute + { +#if false // Not used in unit testing crawling + public bool HighPriorityForActiveFile { get; } +#endif + public string Name { get; } + public string[] WorkspaceKinds { get; } + + public ExportUnitTestingIncrementalAnalyzerProviderAttribute(string name, string[] workspaceKinds) + : base(typeof(IUnitTestingIncrementalAnalyzerProvider)) + { + this.WorkspaceKinds = workspaceKinds; + this.Name = name ?? throw new ArgumentNullException(nameof(name)); +#if false // Not used in unit testing crawling + this.HighPriorityForActiveFile = false; +#endif + } + +#if false // Not used in unit testing crawling + public ExportUnitTestingIncrementalAnalyzerProviderAttribute(bool highPriorityForActiveFile, string name, string[] workspaceKinds) + : this(name, workspaceKinds) + { + this.HighPriorityForActiveFile = highPriorityForActiveFile; + } +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentDifferenceService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentDifferenceService.cs new file mode 100644 index 0000000000000..c37eb35eb9fba --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentDifferenceService.cs @@ -0,0 +1,27 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal class UnitTestingDocumentDifferenceResult + { + public UnitTestingInvocationReasons ChangeType { get; } + public SyntaxNode? ChangedMember { get; } + + public UnitTestingDocumentDifferenceResult(UnitTestingInvocationReasons changeType, SyntaxNode? changedMember = null) + { + ChangeType = changeType; + ChangedMember = changedMember; + } + } + + internal interface IUnitTestingDocumentDifferenceService : ILanguageService + { + UnitTestingDocumentDifferenceResult? GetDifference(Document oldDocument, Document newDocument, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingService.cs new file mode 100644 index 0000000000000..c26e672d992d7 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingService.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. + +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal interface IUnitTestingDocumentTrackingService : IWorkspaceService + { + bool SupportsDocumentTracking { get; } + + /// + /// Get the of the active document. May be null if there is no active document + /// or the active document is not in the workspace. + /// + DocumentId? TryGetActiveDocument(); + + /// + /// Get a read only collection of the s of all the visible documents in the workspace. + /// + ImmutableArray GetVisibleDocuments(); + +#if false // Not used in unit testing crawling + event EventHandler ActiveDocumentChanged; +#endif + + /// + /// Raised when a text buffer that's not part of a workspace is changed. + /// + event EventHandler NonRoslynBufferTextChanged; + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingServiceExtensions.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingServiceExtensions.cs new file mode 100644 index 0000000000000..fc86ae798ba46 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingDocumentTrackingServiceExtensions.cs @@ -0,0 +1,37 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal static class IUnitTestingDocumentTrackingServiceExtensions + { + /// + /// Gets the active the user is currently working in. May be null if + /// there is no active document or the active document is not in this . + /// + public static Document? GetActiveDocument(this IUnitTestingDocumentTrackingService service, Solution solution) + { + // Note: GetDocument checks that the DocId is contained in the solution, and returns null if not. + return solution.GetDocument(service.TryGetActiveDocument()); + } + + /// + /// Get a read only collection of all the unique visible documents in the workspace that are + /// contained within . + /// + public static ImmutableArray GetVisibleDocuments(this IUnitTestingDocumentTrackingService service, Solution solution) + => service.GetVisibleDocuments() + .Select(solution.GetDocument) + .WhereNotNull() + .Distinct() + .ToImmutableArray(); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..ffdd3ed54ea5a --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs @@ -0,0 +1,66 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal interface IUnitTestingIncrementalAnalyzer + { +#if false // Not used in unit testing crawling + Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); + + Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); + Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); + + Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); + + /// + /// Resets all the document state cached by the analyzer. + /// + Task DocumentResetAsync(Document document, CancellationToken cancellationToken); + + Task AnalyzeSyntaxAsync(Document document, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken); +#endif + + Task AnalyzeDocumentAsync( + Document document, +#if false // Not used in unit testing crawling + SyntaxNode bodyOpt, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken); + + Task AnalyzeProjectAsync( + Project project, +#if false // Not used in unit testing crawling + bool semanticsChanged, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken); + + Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); + +#if false // Not used in unit testing crawling + Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); + + Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); + Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); + + /// + /// Resets all the document state cached by the analyzer. + /// + Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken); + + Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken); + + void LogAnalyzerCountSummary(); + int Priority { get; } + void Shutdown(); +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzerProvider.cs new file mode 100644 index 0000000000000..5cddd91b6b4aa --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzerProvider.cs @@ -0,0 +1,15 @@ +// 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. + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal interface IUnitTestingIncrementalAnalyzerProvider + { + IUnitTestingIncrementalAnalyzer? CreateIncrementalAnalyzer( +#if false // Not used in unit testing crawling + Workspace workspace +#endif + ); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerProgressReporter.cs new file mode 100644 index 0000000000000..54a0961af7765 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerProgressReporter.cs @@ -0,0 +1,53 @@ +// 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; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + /// + /// Provide a way to see whether solution crawler is started or not + /// + internal interface IUnitTestingSolutionCrawlerProgressReporter + { + /// + /// Return true if solution crawler is in progress. + /// + bool InProgress { get; } + + /// + /// Raised when solution crawler progress changed + /// + /// Notifications for this event are serialized to preserve order. + /// However, individual event notifications may occur on any thread. + /// + event EventHandler ProgressChanged; + } + + internal readonly struct UnitTestingProgressData + { + public UnitTestingProgressStatus Status { get; } + + /// + /// number of pending work item in the queue. + /// null means N/A for the associated + /// + public int? PendingItemCount { get; } + + public UnitTestingProgressData(UnitTestingProgressStatus type, int? pendingItemCount) + { + Status = type; + PendingItemCount = pendingItemCount; + } + } + + internal enum UnitTestingProgressStatus + { + Started, + Paused, + PendingItemCountUpdated, + Evaluating, + Stopped + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerRegistrationService.cs new file mode 100644 index 0000000000000..4e70c57fea007 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerRegistrationService.cs @@ -0,0 +1,24 @@ +// 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.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + /// + /// Register a solution crawler for a particular workspace + /// + internal interface IUnitTestingSolutionCrawlerRegistrationService : IWorkspaceService + { + IUnitTestingWorkCoordinator Register(Solution solution); + +#if false // Not used in unit testing crawling + void Unregister(Workspace workspace, bool blockingShutdown = false); +#endif + + void AddAnalyzerProvider(IUnitTestingIncrementalAnalyzerProvider provider, UnitTestingIncrementalAnalyzerProviderMetadata metadata); + + bool HasRegisteredAnalyzerProviders { get; } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerService.cs new file mode 100644 index 0000000000000..4166e76b4612a --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingSolutionCrawlerService.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.Collections.Generic; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + /// + /// Provide a way to control solution crawler. + /// + internal interface IUnitTestingSolutionCrawlerService : IWorkspaceService + { + /// + /// Ask solution crawler to re-analyze given s or/and s + /// in given with given . + /// + void Reanalyze(string? workspaceKind, SolutionServices services, IUnitTestingIncrementalAnalyzer analyzer, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false); + + /// + /// Get for the given + /// + IUnitTestingSolutionCrawlerProgressReporter GetProgressReporter(Workspace workspace); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinator.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinator.cs new file mode 100644 index 0000000000000..0cf05fb8afec4 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinator.cs @@ -0,0 +1,15 @@ +// 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. + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal interface IUnitTestingWorkCoordinator + { + void OnWorkspaceChanged(WorkspaceChangeEventArgs args); +#if false // Not used in unit testing crawling + void OnTextDocumentOpened(TextDocumentEventArgs args); + void OnTextDocumentClosed(TextDocumentEventArgs args); +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinatorPriorityService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinatorPriorityService.cs new file mode 100644 index 0000000000000..1c0c45737a2e2 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingWorkCoordinatorPriorityService.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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal interface IUnitTestingWorkCoordinatorPriorityService : ILanguageService + { + /// + /// True if this document is less important than other documents in the project it is + /// contained in, and should have work scheduled for it happen after all other documents + /// in the project. + /// + Task IsLowPriorityAsync(Document document, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingExtensions.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingExtensions.cs new file mode 100644 index 0000000000000..a68a84f5aa2e6 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingExtensions.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.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal static class UnitTestingExtensions + { + public static string ToBase64(this string data) + { + // Write out the message in base64, since it may contain + // user code that can't be represented in xml. (see + // http://vstfdevdiv:8080/web/wi.aspx?pcguid=22f9acc9-569a-41ff-b6ac-fac1b6370209&id=578059) + return Convert.ToBase64String(Encoding.UTF8.GetBytes(data)); + } + + public static string DecodeBase64(this string data) + { + var bytes = Convert.FromBase64String(data); + return Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingGlobalOperationAwareIdleProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingGlobalOperationAwareIdleProcessor.cs new file mode 100644 index 0000000000000..278d0f568df9c --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingGlobalOperationAwareIdleProcessor.cs @@ -0,0 +1,42 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal abstract class UnitTestingGlobalOperationAwareIdleProcessor : UnitTestingIdleProcessor + { + private readonly IGlobalOperationNotificationService _globalOperationNotificationService; + + public UnitTestingGlobalOperationAwareIdleProcessor( + IAsynchronousOperationListener listener, + IGlobalOperationNotificationService globalOperationNotificationService, + TimeSpan backOffTimeSpan, + CancellationToken shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) + { + _globalOperationNotificationService = globalOperationNotificationService; + _globalOperationNotificationService.Started += OnGlobalOperationStarted; + _globalOperationNotificationService.Stopped += OnGlobalOperationStopped; + } + + public virtual void Shutdown() + { + _globalOperationNotificationService.Started -= OnGlobalOperationStarted; + _globalOperationNotificationService.Stopped -= OnGlobalOperationStopped; + } + + private void OnGlobalOperationStarted(object? sender, EventArgs e) + => this.SetIsPaused(isPaused: true); + + private void OnGlobalOperationStopped(object? sender, EventArgs e) + => this.SetIsPaused(isPaused: false); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs new file mode 100644 index 0000000000000..7d92d0de54255 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs @@ -0,0 +1,161 @@ +// 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.Threading; +using System.Threading.Tasks; +using System.Transactions; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal abstract class UnitTestingIdleProcessor + { + private static readonly TimeSpan s_minimumDelay = TimeSpan.FromMilliseconds(50); + + private readonly object _gate = new(); + + protected readonly IAsynchronousOperationListener Listener; + protected readonly CancellationToken CancellationToken; + protected readonly TimeSpan BackOffTimeSpan; + + // points to processor task + private Task? _processorTask; + + // there is one thread that writes to it and one thread reads from it + private SharedStopwatch _timeSinceLastAccess; + + /// + /// Whether or not this processor is paused. As long as it is paused, it will not start executing new work, + /// even if has been met. + /// + private bool _isPaused_doNotAccessDirectly; + + public UnitTestingIdleProcessor( + IAsynchronousOperationListener listener, + TimeSpan backOffTimeSpan, + CancellationToken cancellationToken) + { + Listener = listener; + CancellationToken = cancellationToken; + + BackOffTimeSpan = backOffTimeSpan; + _timeSinceLastAccess = SharedStopwatch.StartNew(); + } + + protected abstract Task WaitAsync(CancellationToken cancellationToken); + protected abstract Task ExecuteAsync(); + + /// + /// Will be called in a serialized fashion (i.e. never concurrently). + /// + protected abstract void OnPaused(); + + protected void Start() + { + Contract.ThrowIfFalse(_processorTask == null); + _processorTask = Task.Factory.SafeStartNewFromAsync(ProcessAsync, CancellationToken, TaskScheduler.Default); + } + + protected void UpdateLastAccessTime() + => _timeSinceLastAccess = SharedStopwatch.StartNew(); + + /// + /// Whether or not we are paused due to a global operation being in effect. + /// + protected bool GetIsPaused() + { + lock (_gate) + return _isPaused_doNotAccessDirectly; + } + + /// + /// Whether or not enough time has passed since the last time we were asked to back off. + /// + protected bool ShouldContinueToBackOff() + => _timeSinceLastAccess.Elapsed < BackOffTimeSpan; + + protected void SetIsPaused(bool isPaused) + { + lock (_gate) + { + // We should never try to transition from paused state to paused state. That would indicate we + // missed some resume call, or that the pause-notification are not serialized. Note: we cannot make + // the opposite assertion. We start in the resumed state, and we might then get a call to resume if + // we were started while in the *middle* of some global operation. + if (isPaused) + Contract.ThrowIfTrue(_isPaused_doNotAccessDirectly); + + _isPaused_doNotAccessDirectly = isPaused; + } + + // Let subclasses know we're paused so they can change what they're doing accordingly. + if (isPaused) + OnPaused(); + } + + /// if the delay compeleted normally; otherwise, if the + /// delay completed due to a request to expedite the delay. + protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySource) + { + while (true) + { + this.CancellationToken.ThrowIfCancellationRequested(); + + // If we're not paused, and enough time has elapsed, then we're done. Otherwise, ensure we wait at + // least s_minimumDelay and check again in the future. + if (!GetIsPaused() && !ShouldContinueToBackOff()) + return true; + + var timeLeft = BackOffTimeSpan - _timeSinceLastAccess.Elapsed; + var delayTimeSpan = TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)); + if (!await expeditableDelaySource.Delay(delayTimeSpan, CancellationToken).ConfigureAwait(false)) + { + // The delay terminated early to accommodate a blocking operation. Make sure to yield so low + // priority (on idle) operations get a chance to be triggered. + // + // 📝 At the time this was discovered, it was not clear exactly why the yield (previously delay) + // was needed in order to avoid live-lock scenarios. + await Task.Yield().ConfigureAwait(false); + return false; + } + } + } + + private async Task ProcessAsync() + { + while (!CancellationToken.IsCancellationRequested) + { + try + { + // wait for next item available + await WaitAsync(CancellationToken).ConfigureAwait(false); + CancellationToken.ThrowIfCancellationRequested(); + + using (Listener.BeginAsyncOperation("ProcessAsync")) + { + // we have items but workspace is busy. wait for idle. + await WaitForIdleAsync(Listener).ConfigureAwait(false); + CancellationToken.ThrowIfCancellationRequested(); + + await ExecuteAsync().ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + // ignore cancellation exception + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e)) + { + // In case any error happen during the execution, don't exit the loop and continue to work on the next item. + } + } + } + + public virtual Task AsyncProcessorTask + => _processorTask ?? Task.CompletedTask; + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerBase.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerBase.cs new file mode 100644 index 0000000000000..be09fca827a1f --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerBase.cs @@ -0,0 +1,91 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal class UnitTestingIncrementalAnalyzerBase : IUnitTestingIncrementalAnalyzer + { + protected UnitTestingIncrementalAnalyzerBase() + { + } + +#if false // Not used in unit testing crawling + + public virtual Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task AnalyzeSyntaxAsync(Document document, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + => Task.CompletedTask; + +#endif + + public virtual Task AnalyzeDocumentAsync( + Document document, +#if false // Not used in unit testing crawling + SyntaxNode bodyOpt, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task AnalyzeProjectAsync( + Project project, +#if false // Not used in unit testing crawling + bool semanticsChanged, +#endif + UnitTestingInvocationReasons reasons, + CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) + => Task.CompletedTask; + +#if false // Not used in unit testing crawling + + public virtual Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellation) + => Task.CompletedTask; + + public virtual Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken) + => Task.CompletedTask; + + public virtual Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + => Task.CompletedTask; + + public void LogAnalyzerCountSummary() + { + } + + /// + /// Order all incremental analyzers below DiagnosticIncrementalAnalyzer + /// + public virtual int Priority => 1; + + public virtual void Shutdown() + { + } + +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs new file mode 100644 index 0000000000000..24bdb9e560d8c --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs @@ -0,0 +1,64 @@ +// 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.Collections.Generic; +using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal class UnitTestingIncrementalAnalyzerProviderMetadata : WorkspaceKindMetadata + { +#if false // Not used in unit testing crawling + public bool HighPriorityForActiveFile { get; } +#endif + public string Name { get; } + + public UnitTestingIncrementalAnalyzerProviderMetadata(IDictionary data) + : base(data) + { +#if false // Not used in unit testing crawling + this.HighPriorityForActiveFile = (bool)data.GetValueOrDefault("HighPriorityForActiveFile"); +#endif + this.Name = (string)data.GetValueOrDefault("Name"); + } + + public UnitTestingIncrementalAnalyzerProviderMetadata( + string name, +#if false // Not used in unit testing crawling + bool highPriorityForActiveFile, +#endif + params string[] workspaceKinds) + : base(workspaceKinds) + { +#if false // Not used in unit testing crawling + this.HighPriorityForActiveFile = highPriorityForActiveFile; +#endif + this.Name = name; + } + + public override bool Equals(object obj) + { + return obj is UnitTestingIncrementalAnalyzerProviderMetadata metadata + && base.Equals(obj) +#if false // Not used in unit testing crawling + && HighPriorityForActiveFile == metadata.HighPriorityForActiveFile +#endif + && Name == metadata.Name; + } + + public override int GetHashCode() + { + var hashCode = 1997033996; + hashCode = hashCode * -1521134295 + base.GetHashCode(); +#if false // Not used in unit testing crawling + hashCode = hashCode * -1521134295 + HighPriorityForActiveFile.GetHashCode(); +#endif + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); + return hashCode; + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons.cs new file mode 100644 index 0000000000000..0a12b39f3ba94 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons.cs @@ -0,0 +1,51 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + [DataContract] + internal partial struct UnitTestingInvocationReasons : IEnumerable + { + public static readonly UnitTestingInvocationReasons Empty = new(ImmutableHashSet.Empty); + + [DataMember(Order = 0)] + private readonly ImmutableHashSet _reasons; + + public UnitTestingInvocationReasons(string reason) + : this(ImmutableHashSet.Create(reason)) + { + } + + public UnitTestingInvocationReasons(ImmutableHashSet reasons) + => _reasons = reasons ?? ImmutableHashSet.Empty; + + public bool IsEmpty => _reasons.IsEmpty; + + public bool Contains(string reason) + => _reasons.Contains(reason); + + public UnitTestingInvocationReasons With(UnitTestingInvocationReasons invocationReasons) + => new(_reasons.Union(invocationReasons._reasons)); + + public UnitTestingInvocationReasons With(string reason) + => new(_reasons.Add(reason)); + + public ImmutableHashSet.Enumerator GetEnumerator() + => _reasons.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _reasons.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _reasons.GetEnumerator(); + + public override string ToString() + => string.Join("|", _reasons ?? ImmutableHashSet.Empty); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons_Constants.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons_Constants.cs new file mode 100644 index 0000000000000..31047c6ab0169 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingInvocationReasons_Constants.cs @@ -0,0 +1,111 @@ +// 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.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal readonly partial struct UnitTestingInvocationReasons + { + public static readonly UnitTestingInvocationReasons DocumentAdded = + new( + ImmutableHashSet.Create( +#if false // Not used in unit testing crawling + UnitTestingPredefinedInvocationReasons.DocumentAdded, + UnitTestingPredefinedInvocationReasons.SyntaxChanged, +#endif + UnitTestingPredefinedInvocationReasons.SemanticChanged)); + + public static readonly UnitTestingInvocationReasons DocumentRemoved = + new( + ImmutableHashSet.Create( +#if false // Not used in unit testing crawling + UnitTestingPredefinedInvocationReasons.DocumentRemoved, + UnitTestingPredefinedInvocationReasons.SyntaxChanged, +#endif + UnitTestingPredefinedInvocationReasons.SemanticChanged, + UnitTestingPredefinedInvocationReasons.HighPriority)); + +#if false // Not used in unit testing crawling + public static readonly UnitTestingInvocationReasons ProjectParseOptionChanged = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.ProjectParseOptionsChanged, + UnitTestingPredefinedInvocationReasons.SyntaxChanged, + UnitTestingPredefinedInvocationReasons.SemanticChanged)); +#endif + + public static readonly UnitTestingInvocationReasons ProjectConfigurationChanged = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.ProjectConfigurationChanged, +#if false // Not used in unit testing crawling + UnitTestingPredefinedInvocationReasons.SyntaxChanged, +#endif + UnitTestingPredefinedInvocationReasons.SemanticChanged)); + +#if false + public static readonly UnitTestingInvocationReasons SolutionRemoved = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.SolutionRemoved, + UnitTestingPredefinedInvocationReasons.DocumentRemoved)); +#endif + +#if false // Not used in unit testing crawling + public static readonly UnitTestingInvocationReasons DocumentOpened = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.DocumentOpened, + UnitTestingPredefinedInvocationReasons.HighPriority)); + + public static readonly UnitTestingInvocationReasons DocumentClosed = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.DocumentClosed, + UnitTestingPredefinedInvocationReasons.HighPriority)); +#endif + + public static readonly UnitTestingInvocationReasons DocumentChanged = + new( + ImmutableHashSet.Create( +#if false // Not used in unit testing crawling + UnitTestingPredefinedInvocationReasons.SyntaxChanged, +#endif + UnitTestingPredefinedInvocationReasons.SemanticChanged)); + + public static readonly UnitTestingInvocationReasons AdditionalDocumentChanged = + new( + ImmutableHashSet.Create( +#if false // Not used in unit testing crawling + UnitTestingPredefinedInvocationReasons.SyntaxChanged, +#endif + UnitTestingPredefinedInvocationReasons.SemanticChanged)); + +#if false // Not used in unit testing crawling + public static readonly UnitTestingInvocationReasons SyntaxChanged = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.SyntaxChanged)); +#endif + + public static readonly UnitTestingInvocationReasons SemanticChanged = + new( + ImmutableHashSet.Create( + UnitTestingPredefinedInvocationReasons.SemanticChanged)); + + public static readonly UnitTestingInvocationReasons Reanalyze = + new(UnitTestingPredefinedInvocationReasons.Reanalyze); + +#if false // Not used in unit testing crawling + public static readonly UnitTestingInvocationReasons ReanalyzeHighPriority = + Reanalyze.With(UnitTestingPredefinedInvocationReasons.HighPriority); + + public static readonly UnitTestingInvocationReasons ActiveDocumentSwitched = + new(UnitTestingPredefinedInvocationReasons.ActiveDocumentSwitched); +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingPredefinedInvocationReasons.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingPredefinedInvocationReasons.cs new file mode 100644 index 0000000000000..fb3bfdbcf1a54 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingPredefinedInvocationReasons.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. + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal static class UnitTestingPredefinedInvocationReasons + { + public const string SemanticChanged = nameof(SemanticChanged); + public const string Reanalyze = nameof(Reanalyze); + public const string ProjectConfigurationChanged = nameof(ProjectConfigurationChanged); + + public const string HighPriority = nameof(HighPriority); + +#if false // Not used in unit testing crawling + public const string DocumentAdded = nameof(DocumentAdded); + public const string DocumentRemoved = nameof(DocumentRemoved); + public const string DocumentOpened = nameof(DocumentOpened); + public const string DocumentClosed = nameof(DocumentClosed); + public const string ActiveDocumentSwitched = nameof(ActiveDocumentSwitched); + public const string ProjectParseOptionsChanged = nameof(ProjectParseOptionsChanged); + public const string SolutionRemoved = nameof(SolutionRemoved); + public const string SyntaxChanged = nameof(SyntaxChanged); +#endif + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerLogger.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerLogger.cs new file mode 100644 index 0000000000000..6939e84a49a6d --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerLogger.cs @@ -0,0 +1,303 @@ +// 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 Microsoft.CodeAnalysis.Internal.Log; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal static class UnitTestingSolutionCrawlerLogger + { + private const string Id = nameof(Id); + private const string Kind = nameof(Kind); + private const string Analyzer = nameof(Analyzer); + private const string DocumentCount = nameof(DocumentCount); + private const string Languages = nameof(Languages); + private const string HighPriority = nameof(HighPriority); + private const string Enabled = nameof(Enabled); + private const string AnalyzerCount = nameof(AnalyzerCount); + private const string PersistentStorage = nameof(PersistentStorage); + private const string GlobalOperation = nameof(GlobalOperation); + private const string HigherPriority = nameof(HigherPriority); + private const string LowerPriority = nameof(LowerPriority); + private const string TopLevel = nameof(TopLevel); + private const string MemberLevel = nameof(MemberLevel); + private const string NewWorkItem = nameof(NewWorkItem); + private const string UpdateWorkItem = nameof(UpdateWorkItem); + private const string ProjectEnqueue = nameof(ProjectEnqueue); + private const string ResetStates = nameof(ResetStates); + private const string ProjectNotExist = nameof(ProjectNotExist); + private const string DocumentNotExist = nameof(DocumentNotExist); + private const string ProcessProject = nameof(ProcessProject); + private const string OpenDocument = nameof(OpenDocument); + private const string CloseDocument = nameof(CloseDocument); + private const string SolutionHash = nameof(SolutionHash); + private const string ProcessDocument = nameof(ProcessDocument); + private const string ProcessDocumentCancellation = nameof(ProcessDocumentCancellation); + private const string ProcessProjectCancellation = nameof(ProcessProjectCancellation); + private const string ActiveFileEnqueue = nameof(ActiveFileEnqueue); + private const string ActiveFileProcessDocument = nameof(ActiveFileProcessDocument); + private const string ActiveFileProcessDocumentCancellation = nameof(ActiveFileProcessDocumentCancellation); + + public static void LogRegistration(int correlationId, string workspaceKind) + { + Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Register, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + m[Kind] = workspaceKind; + })); + } + + public static void LogUnregistration(int correlationId) + { + Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Unregister, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + })); + } + + public static void LogReanalyze( + int correlationId, + IUnitTestingIncrementalAnalyzer analyzer, + int documentCount, + string languages +#if false // Not used in unit testing crawling + , bool highPriority +#endif + ) + { + Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Reanalyze, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + m[Analyzer] = analyzer.ToString(); + m[DocumentCount] = documentCount; +#if false // Not used in unit testing crawling + m[HighPriority] = highPriority; +#endif + m[Languages] = languages; + })); + } + + public static void LogAnalyzers(int correlationId, string workspaceKind, ImmutableArray reordered, bool onlyHighPriorityAnalyzer) + { + if (onlyHighPriorityAnalyzer) + { + LogAnalyzersWorker( + FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzers, FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzer, + correlationId, workspaceKind, reordered); + } + else + { + LogAnalyzersWorker( + FunctionId.IncrementalAnalyzerProcessor_Analyzers, FunctionId.IncrementalAnalyzerProcessor_Analyzer, + correlationId, workspaceKind, reordered); + } + } + + private static void LogAnalyzersWorker( + FunctionId analyzersId, FunctionId analyzerId, int correlationId, string workspaceKind, ImmutableArray reordered) + { + if (workspaceKind == WorkspaceKind.Preview) + { + return; + } + + // log registered analyzers. + Logger.Log(analyzersId, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + m[AnalyzerCount] = reordered.Length; + })); + + foreach (var analyzer in reordered) + { + Logger.Log(analyzerId, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + m[Analyzer] = analyzer.ToString(); + })); + } + } + + public static void LogWorkCoordinatorShutdownTimeout(int correlationId) + { + Logger.Log(FunctionId.WorkCoordinator_ShutdownTimeout, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + })); + } + + public static void LogWorkspaceEvent(CountLogAggregator logAggregator, WorkspaceChangeKind kind) + => logAggregator.IncreaseCount(kind); + + public static void LogWorkCoordinatorShutdown(int correlationId, CountLogAggregator logAggregator) + { + Logger.Log(FunctionId.WorkCoordinator_Shutdown, KeyValueLogMessage.Create(m => + { + m[Id] = correlationId; + + foreach (var kv in logAggregator) + { + var change = kv.Key.ToString(); + m[change] = kv.Value.GetCount(); + } + })); + } + + public static void LogGlobalOperation(CountLogAggregator logAggregator) + => logAggregator.IncreaseCount(GlobalOperation); + + public static void LogActiveFileEnqueue(CountLogAggregator logAggregator) + => logAggregator.IncreaseCount(ActiveFileEnqueue); + + public static void LogWorkItemEnqueue(CountLogAggregator logAggregator, ProjectId _) + => logAggregator.IncreaseCount(ProjectEnqueue); + + public static void LogWorkItemEnqueue( + CountLogAggregator logAggregator, string language, DocumentId? documentId, UnitTestingInvocationReasons reasons, bool lowPriority, SyntaxPath? activeMember, bool added) + { + logAggregator.IncreaseCount(language); + logAggregator.IncreaseCount(added ? NewWorkItem : UpdateWorkItem); + + if (documentId != null) + { + logAggregator.IncreaseCount(activeMember == null ? TopLevel : MemberLevel); + + if (lowPriority) + { + logAggregator.IncreaseCount(LowerPriority); + logAggregator.IncreaseCount(ValueTuple.Create(LowerPriority, documentId.Id)); + } + } + + foreach (var reason in reasons) + { + logAggregator.IncreaseCount(reason); + } + } + + public static void LogHigherPriority(CountLogAggregator logAggregator, Guid documentId) + { + logAggregator.IncreaseCount(HigherPriority); + logAggregator.IncreaseCount(ValueTuple.Create(HigherPriority, documentId)); + } + + public static void LogResetStates(CountLogAggregator logAggregator) + => logAggregator.IncreaseCount(ResetStates); + + public static void LogIncrementalAnalyzerProcessorStatistics(int correlationId, Solution solution, CountLogAggregator logAggregator, ImmutableArray analyzers) + { + Logger.Log(FunctionId.IncrementalAnalyzerProcessor_Shutdown, KeyValueLogMessage.Create(m => + { + var solutionHash = GetSolutionHash(solution); + + m[Id] = correlationId; + m[SolutionHash] = solutionHash.ToString(); + + var statMap = new Dictionary>(); + foreach (var (key, counter) in logAggregator) + { + if (key is string stringKey) + { + m[stringKey] = counter.GetCount(); + } + else if (key is ValueTuple propertyNameAndId) + { + var list = statMap.GetOrAdd(propertyNameAndId.Item1, _ => new List()); + list.Add(counter.GetCount()); + } + else + { + throw ExceptionUtilities.Unreachable(); + } + } + + foreach (var (propertyName, propertyValues) in statMap) + { + var result = StatisticResult.FromList(propertyValues); + + result.WriteTelemetryPropertiesTo(m, prefix: propertyName); + } + })); + +#if false // Not used in unit testing crawling + foreach (var analyzer in analyzers) + { + analyzer.LogAnalyzerCountSummary(); + } +#endif + } + + private static int GetSolutionHash(Solution solution) + { + if (solution != null && solution.FilePath != null) + { + return solution.FilePath.ToLowerInvariant().GetHashCode(); + } + + return 0; + } + + public static void LogProcessCloseDocument(CountLogAggregator logAggregator, Guid documentId) + { + logAggregator.IncreaseCount(CloseDocument); + logAggregator.IncreaseCount(ValueTuple.Create(CloseDocument, documentId)); + } + + public static void LogProcessOpenDocument(CountLogAggregator logAggregator, Guid documentId) + { + logAggregator.IncreaseCount(OpenDocument); + logAggregator.IncreaseCount(ValueTuple.Create(OpenDocument, documentId)); + } + + public static void LogProcessActiveFileDocument(CountLogAggregator logAggregator, Guid _, bool processed) + { + if (processed) + { + logAggregator.IncreaseCount(ActiveFileProcessDocument); + } + else + { + logAggregator.IncreaseCount(ActiveFileProcessDocumentCancellation); + } + } + + public static void LogProcessDocument(CountLogAggregator logAggregator, Guid documentId, bool processed) + { + if (processed) + { + logAggregator.IncreaseCount(ProcessDocument); + } + else + { + logAggregator.IncreaseCount(ProcessDocumentCancellation); + } + + logAggregator.IncreaseCount(ValueTuple.Create(ProcessDocument, documentId)); + } + + public static void LogProcessDocumentNotExist(CountLogAggregator logAggregator) + => logAggregator.IncreaseCount(DocumentNotExist); + + public static void LogProcessProject(CountLogAggregator logAggregator, Guid projectId, bool processed) + { + if (processed) + { + logAggregator.IncreaseCount(ProcessProject); + } + else + { + logAggregator.IncreaseCount(ProcessProjectCancellation); + } + + logAggregator.IncreaseCount(ValueTuple.Create(ProcessProject, projectId)); + } + + public static void LogProcessProjectNotExist(CountLogAggregator logAggregator) + => logAggregator.IncreaseCount(ProjectNotExist); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerProgressReporter.cs new file mode 100644 index 0000000000000..40e9f0046c1c2 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerProgressReporter.cs @@ -0,0 +1,110 @@ +// 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.Threading; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService : IUnitTestingSolutionCrawlerRegistrationService + { + /// + /// Progress reporter + /// + /// this progress reporter is a best effort implementation. it doesn't stop world to find out accurate data + /// + /// what this reporter care is we show start/stop background work and show things are moving or paused + /// without too much cost. + /// + /// due to how solution cralwer calls Start/Stop (see caller of those 2), those 2 can't have a race + /// and that is all we care for this reporter + /// + internal sealed class UnitTestingSolutionCrawlerProgressReporter : IUnitTestingSolutionCrawlerProgressReporter + { + // we use ref count here since solution crawler has multiple queues per priority + // where an item can be enqueued and dequeued independently. + // first item added in any of those queues will cause the "start" event to be sent + // and the very last item processed from those queues will cause "stop" event to be sent + // evaluating and paused is also ref counted since work in the lower priority queue can + // be canceled due to new higher priority work item enqueued to higher queue. + // but before lower priority work actually exit due to cancellation, higher work could + // start processing. causing an overlap. the ref count make sure that exiting lower + // work doesn't flip evaluating state to paused state. + private int _progressStartCount = 0; + private int _progressEvaluateCount = 0; + + public event EventHandler? ProgressChanged; + + public bool InProgress => _progressStartCount > 0; + + public void Start() => ChangeProgressStatus(ref _progressStartCount, UnitTestingProgressStatus.Started); + public void Stop() => ChangeProgressStatus(ref _progressStartCount, UnitTestingProgressStatus.Stopped); + + private void Evaluate() => ChangeProgressStatus(ref _progressEvaluateCount, UnitTestingProgressStatus.Evaluating); + private void Pause() => ChangeProgressStatus(ref _progressEvaluateCount, UnitTestingProgressStatus.Paused); + + public void UpdatePendingItemCount(int pendingItemCount) + { + if (_progressStartCount > 0) + { + var progressData = new UnitTestingProgressData(UnitTestingProgressStatus.PendingItemCountUpdated, pendingItemCount); + OnProgressChanged(progressData); + } + } + + /// + /// Allows the solution crawler to start evaluating work enqueued to it. + /// Returns an IDisposable that the caller must dispose of to indicate that it no longer needs the crawler to continue evaluating. + /// Multiple callers can call into this simultaneously. + /// Only when the last one actually disposes the scope-object will the crawler + /// actually revert back to the paused state where no work proceeds. + /// + public IDisposable GetEvaluatingScope() + => new UnitTestingProgressStatusRAII(this); + + private void ChangeProgressStatus(ref int referenceCount, UnitTestingProgressStatus status) + { + var start = status is UnitTestingProgressStatus.Started or UnitTestingProgressStatus.Evaluating; + if (start ? (Interlocked.Increment(ref referenceCount) == 1) : (Interlocked.Decrement(ref referenceCount) == 0)) + { + var progressData = new UnitTestingProgressData(status, pendingItemCount: null); + OnProgressChanged(progressData); + } + } + + private void OnProgressChanged(UnitTestingProgressData progressData) + => ProgressChanged?.Invoke(this, progressData); + + private readonly struct UnitTestingProgressStatusRAII : IDisposable + { + private readonly UnitTestingSolutionCrawlerProgressReporter _owner; + + public UnitTestingProgressStatusRAII(UnitTestingSolutionCrawlerProgressReporter owner) + { + _owner = owner; + _owner.Evaluate(); + } + + public void Dispose() + => _owner.Pause(); + } + } + + /// + /// reporter that doesn't do anything + /// + private class UnitTestingNullReporter : IUnitTestingSolutionCrawlerProgressReporter + { + public static readonly UnitTestingNullReporter Instance = new(); + + public bool InProgress => false; + + public event EventHandler ProgressChanged + { + add { } + remove { } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerRegistrationService.cs new file mode 100644 index 0000000000000..17a7fa57cd610 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerRegistrationService.cs @@ -0,0 +1,352 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.LegacySolutionEvents; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + [ExportWorkspaceService(typeof(IUnitTestingSolutionCrawlerRegistrationService), ServiceLayer.Host), Shared] + internal partial class UnitTestingSolutionCrawlerRegistrationService : IUnitTestingSolutionCrawlerRegistrationService + { + private const string Default = "*"; + + private readonly object _gate = new(); + private readonly UnitTestingSolutionCrawlerProgressReporter _progressReporter = new(); + + private readonly IAsynchronousOperationListener _listener; + private readonly Dictionary<(string workspaceKind, SolutionServices services), UnitTestingWorkCoordinator> _documentWorkCoordinatorMap = new(); + + private ImmutableDictionary>> _analyzerProviders; + + /// + /// The last solution we've heard about from the . This is used by + /// our to represent the equivalent entity that normally represents. + /// + private Solution _lastReportedSolution = null!; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public UnitTestingSolutionCrawlerRegistrationService( + [ImportMany] IEnumerable> analyzerProviders, + IAsynchronousOperationListenerProvider listenerProvider) + { + _analyzerProviders = analyzerProviders.GroupBy(kv => kv.Metadata.Name).ToImmutableDictionary(g => g.Key, g => g.ToImmutableArray()); + AssertAnalyzerProviders(_analyzerProviders); + + _listener = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerUnitTesting); + } + + /// + /// make sure solution cralwer is registered for the given workspace. + /// + public IUnitTestingWorkCoordinator Register(Solution solution) + { + var workspaceKind = solution.WorkspaceKind; + var solutionServices = solution.Services; + Contract.ThrowIfNull(workspaceKind); + + var correlationId = CorrelationIdFactory.GetNextId(); + + UnitTestingWorkCoordinator? coordinator; + lock (_gate) + { + _lastReportedSolution = solution; + if (!_documentWorkCoordinatorMap.TryGetValue((workspaceKind, solutionServices), out coordinator)) + { + coordinator = new UnitTestingWorkCoordinator( + _listener, + GetAnalyzerProviders(workspaceKind), +#if false // Not used in unit testing crawling + initializeLazily: true, +#endif + new UnitTestingRegistration(this, correlationId, workspaceKind, solutionServices, _progressReporter)); + + _documentWorkCoordinatorMap.Add((workspaceKind, solutionServices), coordinator); + } + } + + UnitTestingSolutionCrawlerLogger.LogRegistration(correlationId, workspaceKind); + return coordinator; + } + +#if false // Not used in unit testing crawling + public void Unregister(Workspace workspace, bool blockingShutdown = false) + { + UnitTestingWorkCoordinator? coordinator; + + lock (_gate) + { + if (!_documentWorkCoordinatorMap.TryGetValue(workspace, out coordinator)) + { + // already unregistered + return; + } + + _documentWorkCoordinatorMap.Remove(workspace); + coordinator.Shutdown(blockingShutdown); + } + + UnitTestingSolutionCrawlerLogger.LogUnregistration(coordinator.CorrelationId); + } +#endif + + public bool HasRegisteredAnalyzerProviders + { + get + { + lock (_gate) + { + return _analyzerProviders.Count > 0; + } + } + } + + public void AddAnalyzerProvider(IUnitTestingIncrementalAnalyzerProvider provider, UnitTestingIncrementalAnalyzerProviderMetadata metadata) + { + // now update all existing work coordinator + lock (_gate) + { + var lazyProvider = new Lazy(() => provider, metadata); + + // update existing map for future solution crawler registration - no need for interlock but this makes add or update easier + ImmutableInterlocked.AddOrUpdate(ref _analyzerProviders, metadata.Name, n => ImmutableArray.Create(lazyProvider), (n, v) => v.Add(lazyProvider)); + + // assert map integrity + AssertAnalyzerProviders(_analyzerProviders); + + // find existing coordinator to update + var lazyProviders = _analyzerProviders[metadata.Name]; + foreach (var ((workspaceKind, solutionServices), coordinator) in _documentWorkCoordinatorMap) + { + Contract.ThrowIfNull(workspaceKind); + + if (!TryGetProvider(workspaceKind, lazyProviders, out var picked) || picked != lazyProvider) + { + // check whether new provider belong to current workspace + continue; + } + + var analyzer = lazyProvider.Value.CreateIncrementalAnalyzer(); + if (analyzer != null) + { + coordinator.AddAnalyzer(analyzer +#if false // Not used in unit testing crawling + , metadata.HighPriorityForActiveFile +#endif + ); + } + } + } + } + + public void Reanalyze(string? workspaceKind, SolutionServices services, IUnitTestingIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) + { + Contract.ThrowIfNull(workspaceKind); + + lock (_gate) + { + if (!_documentWorkCoordinatorMap.TryGetValue((workspaceKind, services), out var coordinator)) + { + // this can happen if solution crawler is already unregistered from workspace. + // one of those example will be VS shutting down so roslyn package is disposed but there is a pending + // async operation. + return; + } + + // no specific projects or documents provided + if (projectIds == null && documentIds == null) + { + var solution = coordinator.Registration.GetSolutionToAnalyze(); + coordinator.Reanalyze(analyzer, new UnitTestingReanalyzeScope(solution.Id) +#if false // Not used in unit testing crawling + , highPriority +#endif + ); + return; + } + + coordinator.Reanalyze(analyzer, new UnitTestingReanalyzeScope(projectIds, documentIds) +#if false // Not used in unit testing crawling + , highPriority +#endif + ); + } + } + + private IEnumerable> GetAnalyzerProviders(string workspaceKind) + { + foreach (var (_, lazyProviders) in _analyzerProviders) + { + // try get provider for the specific workspace kind + if (TryGetProvider(workspaceKind, lazyProviders, out var lazyProvider)) + { + yield return lazyProvider; + continue; + } + + // try get default provider + if (TryGetProvider(Default, lazyProviders, out lazyProvider)) + { + yield return lazyProvider; + } + } + } + + private static bool TryGetProvider( + string kind, + ImmutableArray> lazyProviders, + [NotNullWhen(true)] out Lazy? lazyProvider) + { + // set out param + lazyProvider = null; + + // try find provider for specific workspace kind + if (kind != Default) + { + foreach (var provider in lazyProviders) + { + if (provider.Metadata.WorkspaceKinds?.Any(wk => wk == kind) == true) + { + lazyProvider = provider; + return true; + } + } + + return false; + } + + // try find default provider + foreach (var provider in lazyProviders) + { + if (IsDefaultProvider(provider.Metadata)) + { + lazyProvider = provider; + return true; + } + + return false; + } + + return false; + } + + [Conditional("DEBUG")] + private static void AssertAnalyzerProviders( + ImmutableDictionary>> analyzerProviders) + { +#if DEBUG + // make sure there is duplicated provider defined for same workspace. + var set = new HashSet(); + foreach (var kv in analyzerProviders) + { + foreach (var lazyProvider in kv.Value) + { + if (IsDefaultProvider(lazyProvider.Metadata)) + { + Debug.Assert(set.Add(Default)); + continue; + } + + foreach (var kind in lazyProvider.Metadata.WorkspaceKinds!) + { + Debug.Assert(set.Add(kind)); + } + } + + set.Clear(); + } +#endif + } + + private static bool IsDefaultProvider(UnitTestingIncrementalAnalyzerProviderMetadata providerMetadata) + => providerMetadata.WorkspaceKinds == null || providerMetadata.WorkspaceKinds.Count == 0; + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly UnitTestingSolutionCrawlerRegistrationService _solutionCrawlerRegistrationService; + + internal TestAccessor(UnitTestingSolutionCrawlerRegistrationService solutionCrawlerRegistrationService) + { + _solutionCrawlerRegistrationService = solutionCrawlerRegistrationService; + } + + internal ref ImmutableDictionary>> AnalyzerProviders + => ref _solutionCrawlerRegistrationService._analyzerProviders; + +#if false // Not used in unit testing crawling + internal bool TryGetWorkCoordinator(Workspace workspace, [NotNullWhen(true)] out UnitTestingWorkCoordinator? coordinator) + { + lock (_solutionCrawlerRegistrationService._gate) + { + return _solutionCrawlerRegistrationService._documentWorkCoordinatorMap.TryGetValue(workspace, out coordinator); + } + } + + internal void WaitUntilCompletion(Workspace workspace, ImmutableArray workers) + { + if (TryGetWorkCoordinator(workspace, out var coordinator)) + { + coordinator.GetTestAccessor().WaitUntilCompletion(workers); + } + } + + internal void WaitUntilCompletion(Workspace workspace) + { + if (TryGetWorkCoordinator(workspace, out var coordinator)) + { + coordinator.GetTestAccessor().WaitUntilCompletion(); + } + } +#endif + } + + internal sealed class UnitTestingRegistration + { + private readonly UnitTestingSolutionCrawlerRegistrationService _owner; + + public readonly int CorrelationId; + public readonly string WorkspaceKind; + public readonly SolutionServices Services; + public readonly UnitTestingSolutionCrawlerProgressReporter ProgressReporter; + + public UnitTestingRegistration( + UnitTestingSolutionCrawlerRegistrationService owner, + int correlationId, + string workspaceKind, + SolutionServices solutionServices, + UnitTestingSolutionCrawlerProgressReporter progressReporter) + { + _owner = owner; + CorrelationId = correlationId; + WorkspaceKind = workspaceKind; + Services = solutionServices; + ProgressReporter = progressReporter; + } + + public Solution GetSolutionToAnalyze() + { + lock (_owner._gate) + return _owner._lastReportedSolution; + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerService.cs new file mode 100644 index 0000000000000..e2219c703b78c --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerService.cs @@ -0,0 +1,56 @@ +// 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 Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService : IUnitTestingSolutionCrawlerRegistrationService + { + internal static readonly Option2 EnableSolutionCrawler = new("InternalSolutionCrawlerOptions", "Solution Crawler", defaultValue: true, + storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\Internal\SolutionCrawler\Solution Crawler")); + + /// + /// nested class of since it is tightly coupled with it. + /// + /// is implemented by this class since WorkspaceService doesn't allow a class to implement + /// more than one . + /// + [ExportWorkspaceService(typeof(IUnitTestingSolutionCrawlerService), ServiceLayer.Default), Shared] + internal class UnitTestingSolutionCrawlerService : IUnitTestingSolutionCrawlerService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public UnitTestingSolutionCrawlerService() + { + } + + public void Reanalyze(string? workspaceKind, SolutionServices services, IUnitTestingIncrementalAnalyzer analyzer, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false) + { + // if solution crawler doesn't exist for the given workspace. don't do anything + if (services.GetService() is UnitTestingSolutionCrawlerRegistrationService registration) + { + registration.Reanalyze(workspaceKind, services, analyzer, projectIds, documentIds, highPriority); + } + } + + public IUnitTestingSolutionCrawlerProgressReporter GetProgressReporter(Workspace workspace) + { + // if solution crawler doesn't exist for the given workspace, return null reporter + if (workspace.Services.GetService() is UnitTestingSolutionCrawlerRegistrationService registration) + { + // currently we have only 1 global reporter that are shared by all workspaces. + return registration._progressReporter; + } + + return UnitTestingNullReporter.Instance; + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerTimeSpan.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerTimeSpan.cs new file mode 100644 index 0000000000000..c533057f1567f --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingSolutionCrawlerTimeSpan.cs @@ -0,0 +1,18 @@ +// 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; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal static class UnitTestingSolutionCrawlerTimeSpan + { + public static readonly TimeSpan ActiveFileWorkerBackOff = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan AllFilesWorkerBackOff = TimeSpan.FromMilliseconds(1500); + public static readonly TimeSpan EntireProjectWorkerBackOff = TimeSpan.FromMilliseconds(5000); + public static readonly TimeSpan SemanticChangeBackOff = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan ProjectPropagationBackOff = TimeSpan.FromMilliseconds(500); + public static readonly TimeSpan PreviewBackOff = TimeSpan.FromMilliseconds(500); + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.AbstractUnitTestingPriorityProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.AbstractUnitTestingPriorityProcessor.cs new file mode 100644 index 0000000000000..00c7cc7ecb582 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.AbstractUnitTestingPriorityProcessor.cs @@ -0,0 +1,156 @@ +// 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.Internal.Log; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal sealed partial class UnitTestingSolutionCrawlerRegistrationService + { + internal sealed partial class UnitTestingWorkCoordinator + { + private sealed partial class UnitTestingIncrementalAnalyzerProcessor + { + private abstract class AbstractUnitTestingPriorityProcessor : UnitTestingGlobalOperationAwareIdleProcessor + { + protected readonly UnitTestingIncrementalAnalyzerProcessor Processor; + + private readonly object _gate = new(); + private Lazy> _lazyAnalyzers; + + public AbstractUnitTestingPriorityProcessor( + IAsynchronousOperationListener listener, + UnitTestingIncrementalAnalyzerProcessor processor, + Lazy> lazyAnalyzers, + IGlobalOperationNotificationService globalOperationNotificationService, + TimeSpan backOffTimeSpan, + CancellationToken shutdownToken) + : base(listener, globalOperationNotificationService, backOffTimeSpan, shutdownToken) + { + _lazyAnalyzers = lazyAnalyzers; + + Processor = processor; + Processor._documentTracker.NonRoslynBufferTextChanged += OnNonRoslynBufferTextChanged; + } + + public ImmutableArray Analyzers + { + get + { + lock (_gate) + { + return _lazyAnalyzers.Value; + } + } + } + + public void AddAnalyzer(IUnitTestingIncrementalAnalyzer analyzer) + { + lock (_gate) + { + var analyzers = _lazyAnalyzers.Value; + _lazyAnalyzers = new Lazy>(() => analyzers.Add(analyzer)); + } + } + + protected override void OnPaused() + => UnitTestingSolutionCrawlerLogger.LogGlobalOperation(Processor._logAggregator); + + protected abstract Task HigherQueueOperationTask { get; } + protected abstract bool HigherQueueHasWorkItem { get; } + + protected async Task WaitForHigherPriorityOperationsAsync() + { + using (Logger.LogBlock(FunctionId.WorkCoordinator_WaitForHigherPriorityOperationsAsync, CancellationToken)) + { + while (true) + { + CancellationToken.ThrowIfCancellationRequested(); + + // we wait for global operation and higher queue operation if there is anything going on + await HigherQueueOperationTask.ConfigureAwait(false); + + if (HigherQueueHasWorkItem) + { + // There was still something more important in another queue. Back off again (e.g. + // call UpdateLastAccessTime) then wait that amount of time and check again to see + // if that queue is clear. + UpdateLastAccessTime(); + await WaitForIdleAsync(Listener).ConfigureAwait(false); + continue; + } + + if (GetIsPaused()) + { + // if we're paused, we still want to keep waiting until we become unpaused. After we + // become unpaused though, loop around those to see if there is still high pri work + // to do. + await WaitForIdleAsync(Listener).ConfigureAwait(false); + continue; + } + + // There was no higher queue work item and we're not paused. However, we may not have + // waited long enough to actually satisfy our own backoff-delay. If so, wait until + // we're actually idle. + if (ShouldContinueToBackOff()) + { + // Do the wait. If it returns 'true' then we did the full wait. Loop around again + // to see if there is higher priority work, or if we got paused. + + // However, if it returns 'false' then that means the delay completed quickly + // because some unit/integration test is asking us to expedite our work. In that + // case, just return out immediately so we can process what is in our queue. + if (await WaitForIdleAsync(Listener).ConfigureAwait(false)) + continue; + + // intentional fall-through. + } + + return; + } + } + } + + public override void Shutdown() + { + base.Shutdown(); + + Processor._documentTracker.NonRoslynBufferTextChanged -= OnNonRoslynBufferTextChanged; + +#if false // Not used in unit testing crawling + foreach (var analyzer in Analyzers) + { + analyzer.Shutdown(); + } +#endif + } + + private void OnNonRoslynBufferTextChanged(object? sender, EventArgs e) + { + // There are 2 things incremental processor takes care of + // + // #1 is making sure we delay processing any work until there is enough idle (ex, typing) in host. + // #2 is managing cancellation and pending works. + // + // we used to do #1 and #2 only for Roslyn files. and that is usually fine since most of time solution contains only roslyn files. + // + // but for mixed solution (ex, Roslyn files + HTML + JS + CSS), #2 still makes sense but #1 doesn't. We want + // to pause any work while something is going on in other project types as well. + // + // we need to make sure we play nice with neighbors as well. + // + // now, we don't care where changes are coming from. if there is any change in host, we pause ourselves for a while. + UpdateLastAccessTime(); + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncDocumentWorkItemQueue.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncDocumentWorkItemQueue.cs new file mode 100644 index 0000000000000..fee4971bfa3ea --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncDocumentWorkItemQueue.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.Collections.Generic; +using System.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Internal.Log; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal partial class UnitTestingWorkCoordinator + { + private class UnitTestingAsyncDocumentWorkItemQueue : UnitTestingAsyncWorkItemQueue + { + private readonly Dictionary> _documentWorkQueue = new(); + + public UnitTestingAsyncDocumentWorkItemQueue(UnitTestingSolutionCrawlerProgressReporter progressReporter) + : base(progressReporter) + { + } + + protected override int WorkItemCount_NoLock => _documentWorkQueue.Count; + + protected override bool TryTake_NoLock(DocumentId key, out UnitTestingWorkItem workInfo) + { + workInfo = default; + if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && + documentMap.TryGetValue(key, out workInfo)) + { + documentMap.Remove(key); + + if (documentMap.Count == 0) + { + _documentWorkQueue.Remove(key.ProjectId); + SharedPools.BigDefault>().ClearAndFree(documentMap); + } + + return true; + } + + return false; + } + + protected override bool TryTakeAnyWork_NoLock( + ProjectId? preferableProjectId, +#if false // Not used in unit testing crawling + ProjectDependencyGraph dependencyGraph, + IDiagnosticAnalyzerService? service, +#endif + out UnitTestingWorkItem workItem) + { + // there must be at least one item in the map when this is called unless host is shutting down. + if (_documentWorkQueue.Count == 0) + { + workItem = default; + return false; + } + + var documentId = GetBestDocumentId_NoLock(preferableProjectId +#if false // Not used in unit testing crawling + , dependencyGraph + , service +#endif + ); + if (TryTake_NoLock(documentId, out workItem)) + { + return true; + } + + throw ExceptionUtilities.Unreachable(); + } + + private DocumentId GetBestDocumentId_NoLock( + ProjectId? preferableProjectId +#if false // Not used in unit testing crawling + , ProjectDependencyGraph dependencyGraph + , IDiagnosticAnalyzerService? analyzerService +#endif + ) + { + var projectId = GetBestProjectId_NoLock( + _documentWorkQueue, + preferableProjectId +#if false // Not used in unit testing crawling + , dependencyGraph + , analyzerService +#endif + ); + + var documentMap = _documentWorkQueue[projectId]; + + // explicitly iterate so that we can use struct enumerator. + // Return the first normal priority work item we find. If we don't + // find any, then just return the first low prio item we saw. + DocumentId? lowPriorityDocumentId = null; + foreach (var (documentId, workItem) in documentMap) + { + if (workItem.IsLowPriority) + { + lowPriorityDocumentId = documentId; + } + else + { + return documentId; + } + } + + Contract.ThrowIfNull(lowPriorityDocumentId); + return lowPriorityDocumentId; + } + + protected override bool AddOrReplace_NoLock(UnitTestingWorkItem item) + { + Contract.ThrowIfNull(item.DocumentId); + + Cancel_NoLock(item.DocumentId); + + // see whether we need to update + var key = item.DocumentId; + + // now document work + if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && + documentMap.TryGetValue(key, out var existingWorkItem)) + { + // TODO: should I care about language when replace it? + Debug.Assert(existingWorkItem.Language == item.Language); + + // replace it + documentMap[key] = existingWorkItem.With(item.InvocationReasons, item.ActiveMember, item.SpecificAnalyzers, item.IsRetry, item.AsyncToken); + return false; + } + + // add document map if it is not already there + if (documentMap == null) + { + documentMap = SharedPools.BigDefault>().AllocateAndClear(); + _documentWorkQueue.Add(key.ProjectId, documentMap); + + if (_documentWorkQueue.Count == 1) + { + Logger.Log(FunctionId.WorkCoordinator_AsyncWorkItemQueue_FirstItem); + } + } + + // okay, it is new one + // always hold onto the most recent one for the same document + documentMap.Add(key, item); + + return true; + } + + protected override void Dispose_NoLock() + { + foreach (var map in _documentWorkQueue.Values) + { + foreach (var workItem in map.Values) + { + workItem.AsyncToken.Dispose(); + } + + SharedPools.BigDefault>().ClearAndFree(map); + } + + _documentWorkQueue.Clear(); + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs new file mode 100644 index 0000000000000..e6c9ac0800d44 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs @@ -0,0 +1,113 @@ +// 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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Internal.Log; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal partial class UnitTestingWorkCoordinator + { + private sealed class UnitTestingAsyncProjectWorkItemQueue : UnitTestingAsyncWorkItemQueue + { + private readonly Dictionary _projectWorkQueue = new(); + + public UnitTestingAsyncProjectWorkItemQueue(UnitTestingSolutionCrawlerProgressReporter progressReporter) + : base(progressReporter) + { + } + + protected override int WorkItemCount_NoLock => _projectWorkQueue.Count; + + public override Task WaitAsync(CancellationToken cancellationToken) + { + if (!HasAnyWork) + { + Logger.Log(FunctionId.WorkCoordinator_AsyncWorkItemQueue_LastItem); + } + + return base.WaitAsync(cancellationToken); + } + + protected override bool TryTake_NoLock(ProjectId key, out UnitTestingWorkItem workInfo) + { + if (!_projectWorkQueue.TryGetValue(key, out workInfo)) + { + workInfo = default; + return false; + } + + return _projectWorkQueue.Remove(key); + } + + protected override bool TryTakeAnyWork_NoLock( + ProjectId? preferableProjectId, +#if false // Not used in unit testing crawling + ProjectDependencyGraph dependencyGraph, + IDiagnosticAnalyzerService? analyzerService, +#endif + out UnitTestingWorkItem workItem) + { + // there must be at least one item in the map when this is called unless host is shutting down. + if (_projectWorkQueue.Count == 0) + { + workItem = default; + return false; + } + + var projectId = GetBestProjectId_NoLock( + _projectWorkQueue, preferableProjectId +#if false // Not used in unit testing crawling + , dependencyGraph + , analyzerService +#endif + ); + if (TryTake_NoLock(projectId, out workItem)) + { + return true; + } + + throw ExceptionUtilities.Unreachable(); + } + + protected override bool AddOrReplace_NoLock(UnitTestingWorkItem item) + { + var key = item.ProjectId; + Cancel_NoLock(key); + // now document work + + // see whether we need to update + if (_projectWorkQueue.TryGetValue(key, out var existingWorkItem)) + { + // replace it. + _projectWorkQueue[key] = existingWorkItem.With(item.InvocationReasons, item.ActiveMember, item.SpecificAnalyzers, item.IsRetry, item.AsyncToken); + return false; + } + + // okay, it is new one + // always hold onto the most recent one for the same project + _projectWorkQueue.Add(key, item); + + return true; + } + + protected override void Dispose_NoLock() + { + foreach (var workItem in _projectWorkQueue.Values) + { + workItem.AsyncToken.Dispose(); + } + + _projectWorkQueue.Clear(); + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs new file mode 100644 index 0000000000000..4ca5de0a1b04a --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs @@ -0,0 +1,311 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal partial class UnitTestingWorkCoordinator + { + private abstract class UnitTestingAsyncWorkItemQueue : IDisposable + where TKey : class + { + private readonly object _gate = new(); + private readonly SemaphoreSlim _semaphore; + private bool _disposed; + + private readonly UnitTestingSolutionCrawlerProgressReporter _progressReporter; + + // map containing cancellation source for the item given out. + private readonly Dictionary _cancellationMap = new(); + + public UnitTestingAsyncWorkItemQueue(UnitTestingSolutionCrawlerProgressReporter progressReporter) + { + _semaphore = new SemaphoreSlim(initialCount: 0); + + _progressReporter = progressReporter; + } + + protected abstract int WorkItemCount_NoLock { get; } + + protected abstract void Dispose_NoLock(); + + protected abstract bool AddOrReplace_NoLock(UnitTestingWorkItem item); + + protected abstract bool TryTake_NoLock(TKey key, out UnitTestingWorkItem workInfo); + + protected abstract bool TryTakeAnyWork_NoLock( + ProjectId? preferableProjectId, +#if false // Not used in unit testing crawling + ProjectDependencyGraph dependencyGraph, + IDiagnosticAnalyzerService? service, +#endif + out UnitTestingWorkItem workItem); + + public int WorkItemCount + { + get + { + lock (_gate) + { + return WorkItemCount_NoLock; + } + } + } + + public bool HasAnyWork + { + get + { + lock (_gate) + { + return WorkItemCount_NoLock > 0; + } + } + } + + public virtual Task WaitAsync(CancellationToken cancellationToken) + => _semaphore.WaitAsync(cancellationToken); + + public bool AddOrReplace(UnitTestingWorkItem item) + { + lock (_gate) + { + if (_disposed) + { + // The work queue was shut down, so mark the request as complete and return false to + // indicate the work was not queued. + item.AsyncToken.Dispose(); + return false; + } + + if (AddOrReplace_NoLock(item)) + { + // the item is new item that got added to the queue. + // let solution crawler progress report to know about new item enqueued. + // progress reporter will take care of nested/overlapped works by itself + // + // order of events is as follow + // 1. first item added by AddOrReplace which is the point where progress start. + // 2. bunch of other items added or replaced (workitem in the queue > 0) + // 3. items start dequeued to be processed by TryTake or TryTakeAnyWork + // 4. once item is done processed, it is marked as done by MarkWorkItemDoneFor + // 5. all items in the queue are dequeued (workitem in the queue == 0) + // but there can be still work in progress + // 6. all works are considered done when last item is marked done by MarkWorkItemDoneFor + // and at the point, we will set progress to stop. + _progressReporter.Start(); + + // increase count + _semaphore.Release(); + return true; + } + + return false; + } + } + + public void MarkWorkItemDoneFor(object key) + { + lock (_gate) + { + // just remove cancellation token from the map. + // the cancellation token might be passed out to other service + // so don't call cancel on the source only because we are done using it. + _cancellationMap.Remove(key); + + // every works enqueued by "AddOrReplace" will be processed + // at some point, and when it is processed, this method will be called to mark + // work has been done. + _progressReporter.Stop(); + } + } + + public void RequestCancellationOnRunningTasks() + { + List? cancellations; + lock (_gate) + { + // request to cancel all running works + cancellations = CancelAll_NoLock(); + } + + RaiseCancellation_NoLock(cancellations); + } + + public void Dispose() + { + List? cancellations; + lock (_gate) + { + _disposed = true; + + // here we don't need to care about progress reporter since + // it will be only called when host is shutting down. + // we do the below since we want to kill any pending tasks + Dispose_NoLock(); + + cancellations = CancelAll_NoLock(); + } + + RaiseCancellation_NoLock(cancellations); + } + + private static void RaiseCancellation_NoLock(List? cancellations) + { + if (cancellations == null) + { + return; + } + + // cancel can cause outer code to be run inlined, run it outside of the lock. + cancellations.Do(s => s.Cancel()); + } + + private List? CancelAll_NoLock() + { + // nothing to do + if (_cancellationMap.Count == 0) + { + return null; + } + + // make a copy + var cancellations = _cancellationMap.Values.ToList(); + + // clear cancellation map + _cancellationMap.Clear(); + + return cancellations; + } + + protected void Cancel_NoLock(object key) + { + if (_cancellationMap.TryGetValue(key, out var source)) + { + source.Cancel(); + _cancellationMap.Remove(key); + } + } + + public bool TryTake(TKey key, out UnitTestingWorkItem workInfo, out CancellationToken cancellationToken) + { + lock (_gate) + { + if (TryTake_NoLock(key, out workInfo)) + { + cancellationToken = GetNewCancellationToken_NoLock(key); + workInfo.AsyncToken.Dispose(); + return true; + } + else + { + cancellationToken = CancellationToken.None; + return false; + } + } + } + + public bool TryTakeAnyWork( + ProjectId? preferableProjectId, +#if false // Not used in unit testing crawling + ProjectDependencyGraph dependencyGraph, + IDiagnosticAnalyzerService? analyzerService, +#endif + out UnitTestingWorkItem workItem, + out CancellationToken cancellationToken) + { + lock (_gate) + { + // there must be at least one item in the map when this is called unless host is shutting down. + if (TryTakeAnyWork_NoLock(preferableProjectId, +#if false // Not used in unit testing crawling + dependencyGraph, + analyzerService, +#endif + out workItem)) + { + cancellationToken = GetNewCancellationToken_NoLock(workItem.Key); + workItem.AsyncToken.Dispose(); + return true; + } + else + { + cancellationToken = CancellationToken.None; + return false; + } + } + } + + protected CancellationToken GetNewCancellationToken_NoLock(object key) + { + Debug.Assert(!_cancellationMap.ContainsKey(key)); + + var source = new CancellationTokenSource(); + _cancellationMap.Add(key, source); + + return source.Token; + } + + protected static ProjectId GetBestProjectId_NoLock( + Dictionary workQueue, + ProjectId? projectId +#if false // Not used in unit testing crawling + , ProjectDependencyGraph dependencyGraph + , IDiagnosticAnalyzerService? analyzerService +#endif + ) + { + if (projectId != null) + { + if (workQueue.ContainsKey(projectId)) + { + return projectId; + } + +#if false // Not used in unit testing crawling + // prefer project that directly depends on the given project and has diagnostics as next project to + // process + foreach (var dependingProjectId in dependencyGraph.GetProjectsThatDirectlyDependOnThisProject(projectId)) + { + if (workQueue.ContainsKey(dependingProjectId) && analyzerService?.ContainsDiagnostics(Workspace, dependingProjectId) == true) + { + return dependingProjectId; + } + } +#endif + } + +#if false // Not used in unit testing crawling + // prefer a project that has diagnostics as next project to process. + foreach (var pendingProjectId in workQueue.Keys) + { + if (analyzerService?.ContainsDiagnostics(Workspace, pendingProjectId) == true) + { + return pendingProjectId; + } + } +#endif + + // explicitly iterate so that we can use struct enumerator + foreach (var pair in workQueue) + { + return pair.Key; + } + + throw ExceptionUtilities.Unreachable(); + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingHighPriorityProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingHighPriorityProcessor.cs new file mode 100644 index 0000000000000..06fe38ee8aa55 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingHighPriorityProcessor.cs @@ -0,0 +1,242 @@ +// 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.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal sealed partial class UnitTestingSolutionCrawlerRegistrationService + { + internal sealed partial class UnitTestingWorkCoordinator + { + private sealed partial class UnitTestingIncrementalAnalyzerProcessor + { +#if false // Not used in unit testing crawling + private sealed class UnitTestingHighPriorityProcessor : UnitTestingIdleProcessor + { + private readonly UnitTestingIncrementalAnalyzerProcessor _processor; + private readonly UnitTestingAsyncDocumentWorkItemQueue _workItemQueue; + private readonly object _gate = new(); + + private Lazy> _lazyAnalyzers; + + // whether this processor is running or not + private Task _running; + + public UnitTestingHighPriorityProcessor( + IAsynchronousOperationListener listener, + UnitTestingIncrementalAnalyzerProcessor processor, + Lazy> lazyAnalyzers, + TimeSpan backOffTimeSpan, + CancellationToken shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) + { + _processor = processor; + _lazyAnalyzers = lazyAnalyzers; + + _running = Task.CompletedTask; + _workItemQueue = new UnitTestingAsyncDocumentWorkItemQueue(processor._registration.ProgressReporter); + + Start(); + } + + protected override void OnPaused() + { + } + + public ImmutableArray Analyzers + { + get + { + lock (_gate) + { + return _lazyAnalyzers.Value; + } + } + } + + public Task Running => _running; + + public int WorkItemCount => _workItemQueue.WorkItemCount; + public bool HasAnyWork => _workItemQueue.HasAnyWork; + + public void AddAnalyzer(IUnitTestingIncrementalAnalyzer analyzer) + { + lock (_gate) + { + var analyzers = _lazyAnalyzers.Value; + _lazyAnalyzers = new Lazy>(() => analyzers.Add(analyzer)); + } + } + + public void Enqueue(UnitTestingWorkItem item) + { + Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item"); + + // Don't enqueue item if we don't have any high priority analyzers + if (Analyzers.IsEmpty) + { + return; + } + + // we only put workitem in high priority queue if there is a text change. + // this is to prevent things like opening a file, changing in other files keep enqueuing + // expensive high priority work. + if (!item.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.SyntaxChanged)) + { + return; + } + + if (!_processor._documentTracker.SupportsDocumentTracking + && _processor._registration.WorkspaceKind is WorkspaceKind.RemoteWorkspace) + { + Debug.Fail($"Unexpected use of '{nameof(ExportUnitTestingIncrementalAnalyzerProviderAttribute.HighPriorityForActiveFile)}' in workspace kind '{_processor._registration.WorkspaceKind}' that cannot support active file tracking."); + } + + // check whether given item is for active document, otherwise, nothing to do here + if (_processor._documentTracker.TryGetActiveDocument() != item.DocumentId) + { + return; + } + + // we need to clone due to waiter + EnqueueActiveFileItem(item.WithAsyncToken(Listener.BeginAsyncOperation("ActiveFile"))); + } + + private void EnqueueActiveFileItem(UnitTestingWorkItem item) + { + Contract.ThrowIfNull(item.DocumentId); + + UpdateLastAccessTime(); + var added = _workItemQueue.AddOrReplace(item); + + Logger.Log(FunctionId.WorkCoordinator_ActiveFileEnqueue, s_enqueueLogger, Environment.TickCount, item.DocumentId, !added); + UnitTestingSolutionCrawlerLogger.LogActiveFileEnqueue(_processor._logAggregator); + } + + protected override Task WaitAsync(CancellationToken cancellationToken) + => _workItemQueue.WaitAsync(cancellationToken); + + protected override async Task ExecuteAsync() + { + Debug.Assert(!Analyzers.IsEmpty); + + if (CancellationToken.IsCancellationRequested) + { + return; + } + + var source = new TaskCompletionSource(); + try + { + // mark it as running + _running = source.Task; + // okay, there must be at least one item in the map + // see whether we have work item for the document + Contract.ThrowIfFalse(GetNextWorkItem(out var workItem, out var documentCancellation)); + + var solution = _processor._registration.GetSolutionToAnalyze(); + + // okay now we have work to do + await ProcessDocumentAsync(solution, Analyzers, workItem, documentCancellation).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable; + } + finally + { + // mark it as done running + source.SetResult(null); + } + } + + private bool GetNextWorkItem(out UnitTestingWorkItem workItem, out CancellationToken cancellationToken) + { + // GetNextWorkItem since it can't fail. we still return bool to confirm that this never fail. + var documentId = _processor._documentTracker.TryGetActiveDocument(); + if (documentId != null) + { + if (_workItemQueue.TryTake(documentId, out workItem, out cancellationToken)) + { + return true; + } + } + + return _workItemQueue.TryTakeAnyWork( + preferableProjectId: null, +#if false // Not used in unit testing crawling + dependencyGraph: _processor.DependencyGraph, + analyzerService: _processor.DiagnosticAnalyzerService, +#endif + workItem: out workItem, + cancellationToken: out cancellationToken); + } + + private async Task ProcessDocumentAsync(Solution solution, ImmutableArray analyzers, UnitTestingWorkItem workItem, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(workItem.DocumentId); + + if (CancellationToken.IsCancellationRequested) + { + return; + } + + var processedEverything = false; + var documentId = workItem.DocumentId; + + try + { + using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken)) + { + var document = solution.GetDocument(documentId); + if (document != null) + { + await _processor.ProcessDocumentAnalyzersAsync(document, analyzers, workItem, cancellationToken).ConfigureAwait(false); + } + + if (!cancellationToken.IsCancellationRequested) + { + processedEverything = true; + } + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable; + } + finally + { + // we got cancelled in the middle of processing the document. + // let's make sure newly enqueued work item has all the flag needed. + // Avoid retry attempts after cancellation is requested, since work will not be processed + // after that point. + if (!processedEverything && !CancellationToken.IsCancellationRequested) + { + _workItemQueue.AddOrReplace(workItem.Retry(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); + } + + UnitTestingSolutionCrawlerLogger.LogProcessActiveFileDocument(_processor._logAggregator, documentId.Id, processedEverything); + + // remove one that is finished running + _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId); + } + } + + public void Shutdown() + => _workItemQueue.Dispose(); + } +#endif + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingIncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingIncrementalAnalyzerProcessor.cs new file mode 100644 index 0000000000000..c9d9733fa5c55 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingIncrementalAnalyzerProcessor.cs @@ -0,0 +1,530 @@ +// 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.Diagnostics.CodeAnalysis; +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.Internal.Log; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal partial class UnitTestingWorkCoordinator + { + private partial class UnitTestingIncrementalAnalyzerProcessor + { + private static readonly Func s_enqueueLogger = EnqueueLogger; + + private readonly UnitTestingRegistration _registration; + private readonly IAsynchronousOperationListener _listener; + private readonly IUnitTestingDocumentTrackingService _documentTracker; + private readonly IProjectCacheService? _cacheService; + +#if false // Not used in unit testing crawling + private readonly UnitTestingHighPriorityProcessor _highPriorityProcessor; +#endif + private readonly UnitTestingNormalPriorityProcessor _normalPriorityProcessor; + private readonly UnitTestingLowPriorityProcessor _lowPriorityProcessor; + +#if false // Not used in unit testing crawling + // NOTE: IDiagnosticAnalyzerService can be null in test environment. + private readonly Lazy _lazyDiagnosticAnalyzerService; +#endif + + /// + /// The keys in this are either a string or a (string, Guid) tuple. See + /// for what is writing this out. + /// + private CountLogAggregator _logAggregator = new(); + + public UnitTestingIncrementalAnalyzerProcessor( + IAsynchronousOperationListener listener, + IEnumerable> analyzerProviders, +#if false // Not used in unit testing crawling + bool initializeLazily, +#endif + UnitTestingRegistration registration, + TimeSpan highBackOffTimeSpan, + TimeSpan normalBackOffTimeSpan, + TimeSpan lowBackOffTimeSpan, + CancellationToken shutdownToken) + { + _listener = listener; + _registration = registration; + _cacheService = registration.Services.GetService(); + +#if false // Not used in unit testing crawling + _lazyDiagnosticAnalyzerService = new Lazy(() => GetDiagnosticAnalyzerService(analyzerProviders)); +#endif + + var analyzersGetter = new UnitTestingAnalyzersGetter(analyzerProviders); + + // create analyzers lazily. +#if false // Not used in unit testing crawling + var lazyActiveFileAnalyzers = new Lazy>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: true)); +#endif + var lazyAllAnalyzers = new Lazy>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: false)); + +#if false // Not used in unit testing crawling + if (!initializeLazily) + { + // realize all analyzer right away + _ = lazyActiveFileAnalyzers.Value; + _ = lazyAllAnalyzers.Value; + } +#endif + + // event and worker queues + _documentTracker = _registration.Services.GetRequiredService(); + + var globalNotificationService = _registration.Services.GetRequiredService(); + +#if false // Not used in unit testing crawling + _highPriorityProcessor = new UnitTestingHighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpan, shutdownToken); +#endif + _normalPriorityProcessor = new UnitTestingNormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpan, shutdownToken); + _lowPriorityProcessor = new UnitTestingLowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpan, shutdownToken); + } + +#if false // Not used in unit testing crawling + private static IDiagnosticAnalyzerService? GetDiagnosticAnalyzerService(IEnumerable> analyzerProviders) + { + // alternatively, we could just MEF import IDiagnosticAnalyzerService directly + // this can be null in test env. + return (IDiagnosticAnalyzerService?)analyzerProviders.Where(p => p.Value is IDiagnosticAnalyzerService).SingleOrDefault()?.Value; + } +#endif + + private static ImmutableArray GetIncrementalAnalyzers(UnitTestingRegistration registration, UnitTestingAnalyzersGetter analyzersGetter, bool onlyHighPriorityAnalyzer) + { + var orderedAnalyzers = analyzersGetter.GetOrderedAnalyzers(registration.WorkspaceKind, registration.Services, onlyHighPriorityAnalyzer); + + UnitTestingSolutionCrawlerLogger.LogAnalyzers(registration.CorrelationId, registration.WorkspaceKind, orderedAnalyzers, onlyHighPriorityAnalyzer); + return orderedAnalyzers; + } + + public void Enqueue(UnitTestingWorkItem item) + { + Contract.ThrowIfNull(item.DocumentId); + +#if false // Not used in unit testing crawling + _highPriorityProcessor.Enqueue(item); +#endif + _normalPriorityProcessor.Enqueue(item); + _lowPriorityProcessor.Enqueue(item); + + ReportPendingWorkItemCount(); + } + + public void AddAnalyzer( + IUnitTestingIncrementalAnalyzer analyzer +#if false // Not used in unit testing crawling + , bool highPriorityForActiveFile +#endif + ) + { +#if false // Not used in unit testing crawling + if (highPriorityForActiveFile) + { + _highPriorityProcessor.AddAnalyzer(analyzer); + } +#endif + + _normalPriorityProcessor.AddAnalyzer(analyzer); + _lowPriorityProcessor.AddAnalyzer(analyzer); + } + + public void Shutdown() + { +#if false // Not used in unit testing crawling + _highPriorityProcessor.Shutdown(); +#endif + _normalPriorityProcessor.Shutdown(); + _lowPriorityProcessor.Shutdown(); + } + + public ImmutableArray Analyzers => _normalPriorityProcessor.Analyzers; + +#if false // Not used in unit testing crawling + private ProjectDependencyGraph DependencyGraph => _registration.GetSolutionToAnalyze().GetProjectDependencyGraph(); + private IDiagnosticAnalyzerService? DiagnosticAnalyzerService => _lazyDiagnosticAnalyzerService?.Value; +#endif + + public Task AsyncProcessorTask + { + get + { + return Task.WhenAll( +#if false // Not used in unit testing crawling + _highPriorityProcessor.AsyncProcessorTask, +#endif + _normalPriorityProcessor.AsyncProcessorTask, + _lowPriorityProcessor.AsyncProcessorTask); + } + } + + private IDisposable EnableCaching(ProjectId projectId) + => _cacheService?.EnableCaching(projectId) ?? UnitTestingNullDisposable.Instance; + +#if false // Not used in unit testing crawling + private IEnumerable GetOpenDocumentIds() + => _registration.Workspace.GetOpenDocumentIds(); +#endif + + private void ResetLogAggregator() + => _logAggregator = new CountLogAggregator(); + + private void ReportPendingWorkItemCount() + { + var pendingItemCount = +#if false // Not used in unit testing crawling + _highPriorityProcessor.WorkItemCount + +#endif + _normalPriorityProcessor.WorkItemCount + _lowPriorityProcessor.WorkItemCount; + _registration.ProgressReporter.UpdatePendingItemCount(pendingItemCount); + } + + private async Task ProcessDocumentAnalyzersAsync( + TextDocument textDocument, ImmutableArray analyzers, UnitTestingWorkItem workItem, CancellationToken cancellationToken) + { +#if false // Not used in unit testing crawling + // process special active document switched request, if any. + if (ProcessActiveDocumentSwitched(analyzers, workItem, textDocument, cancellationToken)) + { + return; + } +#endif + + // process all analyzers for each categories in this order - syntax, body, document + var reasons = workItem.InvocationReasons; + +#if false // Not used in unit testing crawling + if (workItem.MustRefresh || reasons.Contains(UnitTestingPredefinedInvocationReasons.SyntaxChanged)) + { + await RunAnalyzersAsync(analyzers, textDocument, workItem, (analyzer, document, cancellationToken) => + AnalyzeSyntaxAsync(analyzer, document, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); + } +#endif + + if (textDocument is not Document document) + { + // Semantic analysis is not supported for non-source documents. + return; + } + + if ( +#if false // Not used in unit testing crawling + workItem.MustRefresh || +#endif + reasons.Contains(UnitTestingPredefinedInvocationReasons.SemanticChanged)) + { + await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => + analyzer.AnalyzeDocumentAsync(document, +#if false // Not used in unit testing crawling + bodyOpt: null, +#endif + reasons, + cancellationToken), cancellationToken).ConfigureAwait(false); + } + else + { + // if we don't need to re-analyze whole body, see whether we need to at least re-analyze one method. + await RunBodyAnalyzersAsync(analyzers, workItem, document, cancellationToken).ConfigureAwait(false); + } + + return; + +#if false // Not used in unit testing crawling + static async Task AnalyzeSyntaxAsync(IUnitTestingIncrementalAnalyzer analyzer, TextDocument textDocument, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + { + if (textDocument is Document document) + { + await analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken).ConfigureAwait(false); + } + else + { + await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, reasons, cancellationToken).ConfigureAwait(false); + } + } +#endif + +#if false // Not used in unit testing crawling + bool ProcessActiveDocumentSwitched(ImmutableArray analyzers, UnitTestingWorkItem workItem, TextDocument document, CancellationToken cancellationToken) + { + try + { + if (!workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.ActiveDocumentSwitched)) + { + return false; + } + + await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => + analyzer.ActiveDocumentSwitchedAsync(document, cancellationToken), cancellationToken).ConfigureAwait(false); + + return true; + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable; + } + } +#endif + } + + private async Task RunAnalyzersAsync( + ImmutableArray analyzers, + T value, + UnitTestingWorkItem workItem, + Func runnerAsync, + CancellationToken cancellationToken) + { + using var evaluating = _registration.ProgressReporter.GetEvaluatingScope(); + + ReportPendingWorkItemCount(); + + // Check if the work item is specific to some incremental analyzer(s). + var analyzersToExecute = workItem.GetApplicableAnalyzers(analyzers) ?? analyzers; + foreach (var analyzer in analyzersToExecute) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var local = analyzer; + if (local == null) + { + return; + } + + await GetOrDefaultAsync(value, async (v, c) => + { + await runnerAsync(local, v, c).ConfigureAwait(false); + return (object?)null; + }, cancellationToken).ConfigureAwait(false); + } + } + + private async Task RunBodyAnalyzersAsync(ImmutableArray analyzers, UnitTestingWorkItem workItem, Document document, CancellationToken cancellationToken) + { + try + { + var root = await GetOrDefaultAsync(document, (d, c) => d.GetSyntaxRootAsync(c), cancellationToken).ConfigureAwait(false); + var syntaxFactsService = document.GetLanguageService(); + var reasons = workItem.InvocationReasons; + if (root == null || syntaxFactsService == null) + { + // as a fallback mechanism, if we can't run one method body due to some missing service, run whole document analyzer. + await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => + analyzer.AnalyzeDocumentAsync( + document, +#if false // Not used in unit testing crawling + null, +#endif + reasons, + cancellationToken), cancellationToken).ConfigureAwait(false); + return; + } + +#if false // Not used in unit testing crawling + // check whether we know what body has changed. currently, this is an optimization toward typing case. if there are more than one body changes + // it will be considered as semantic change and whole document analyzer will take care of that case. + var activeMember = GetMemberNode(syntaxFactsService, root, workItem.ActiveMember); + if (activeMember == null) + { + // no active member means, change is out side of a method body, but it didn't affect semantics (such as change in comment) + // in that case, we update whole document (just this document) so that we can have updated locations. + await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => + analyzer.AnalyzeDocumentAsync(document, null, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); + return; + } +#endif + + // re-run just the body + await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => + analyzer.AnalyzeDocumentAsync( + document, +#if false // Not used in unit testing crawling + activeMember, +#endif + reasons, + cancellationToken), cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + } + + private static async Task GetOrDefaultAsync(TData value, Func> funcAsync, CancellationToken cancellationToken) + where TResult : class + { + try + { + return await funcAsync(value, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + return null; + } + catch (AggregateException e) when (ReportWithoutCrashUnlessAllCanceledAndPropagate(e)) + { + return null; + } + catch (Exception e) when (FatalError.ReportAndPropagate(e)) + { + // TODO: manage bad workers like what code actions does now + throw ExceptionUtilities.Unreachable(); + } + + static bool ReportWithoutCrashUnlessAllCanceledAndPropagate(AggregateException aggregate) + { + var flattened = aggregate.Flatten(); + if (flattened.InnerExceptions.All(e => e is OperationCanceledException)) + { + return true; + } + + return FatalError.ReportAndPropagate(flattened); + } + } + +#if false // Not used in unit testing crawling + private static SyntaxNode? GetMemberNode(ISyntaxFactsService service, SyntaxNode? root, SyntaxPath? memberPath) + { + if (root == null || memberPath == null) + { + return null; + } + + if (!memberPath.TryResolve(root, out SyntaxNode? memberNode)) + { + return null; + } + + return service.IsMethodLevelMember(memberNode) ? memberNode : null; + } +#endif + + private static string EnqueueLogger(int tick, object documentOrProjectId, bool replaced) + { + if (documentOrProjectId is DocumentId documentId) + { + return $"Tick:{tick}, {documentId}, {documentId.ProjectId}, Replaced:{replaced}"; + } + + return $"Tick:{tick}, {documentOrProjectId}, Replaced:{replaced}"; + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly UnitTestingIncrementalAnalyzerProcessor _incrementalAnalyzerProcessor; + + internal TestAccessor(UnitTestingIncrementalAnalyzerProcessor incrementalAnalyzerProcessor) + { + _incrementalAnalyzerProcessor = incrementalAnalyzerProcessor; + } + + internal void WaitUntilCompletion(ImmutableArray analyzers, List items) + { + _incrementalAnalyzerProcessor._normalPriorityProcessor.GetTestAccessor().WaitUntilCompletion(analyzers, items); + + var projectItems = items.Select(i => i.ToProjectWorkItem(EmptyAsyncToken.Instance)); + _incrementalAnalyzerProcessor._lowPriorityProcessor.GetTestAccessor().WaitUntilCompletion(analyzers, items); + } + + internal void WaitUntilCompletion() + { + _incrementalAnalyzerProcessor._normalPriorityProcessor.GetTestAccessor().WaitUntilCompletion(); + _incrementalAnalyzerProcessor._lowPriorityProcessor.GetTestAccessor().WaitUntilCompletion(); + } + } + + private class UnitTestingNullDisposable : IDisposable + { + public static readonly IDisposable Instance = new UnitTestingNullDisposable(); + + public void Dispose() { } + } + + private class UnitTestingAnalyzersGetter + { + private readonly List> _analyzerProviders; + private readonly Dictionary<(string workspaceKind, SolutionServices services), ImmutableArray< +#if false // Not used in unit testing crawling + (IUnitTestingIncrementalAnalyzer analyzer, bool highPriorityForActiveFile) +#else + IUnitTestingIncrementalAnalyzer +#endif + >> _analyzerMap = new(); + + public UnitTestingAnalyzersGetter(IEnumerable> analyzerProviders) + { + _analyzerProviders = analyzerProviders.ToList(); + } + + public ImmutableArray GetOrderedAnalyzers(string workspaceKind, SolutionServices services, bool onlyHighPriorityAnalyzer) + { + lock (_analyzerMap) + { + if (!_analyzerMap.TryGetValue((workspaceKind, services), out var analyzers)) + { +#if false // Not used in unit testing crawling + // Sort list so DiagnosticIncrementalAnalyzers (if any) come first. + analyzers = _analyzerProviders.Select(p => (analyzer: p.Value.CreateIncrementalAnalyzer(), highPriorityForActiveFile: p.Metadata.HighPriorityForActiveFile)) + .Where(t => t.analyzer != null) + .OrderBy(t => t.analyzer!.Priority) + .ToImmutableArray()!; +#else + analyzers = _analyzerProviders + .Select(p => p.Value.CreateIncrementalAnalyzer()) + .WhereNotNull() + .ToImmutableArray(); +#endif + + _analyzerMap[(workspaceKind, services)] = analyzers; + } + + if (onlyHighPriorityAnalyzer) + { +#if false // Not used in unit testing crawling + // include only high priority analyzer for active file + return analyzers.SelectAsArray(t => t.highPriorityForActiveFile, t => t.analyzer); +#else + return ImmutableArray.Empty; +#endif + } + +#if false // Not used in unit testing crawling + // return all analyzers + return analyzers.Select(t => t.analyzer).ToImmutableArray(); +#else + return analyzers; +#endif + } + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs new file mode 100644 index 0000000000000..c1ce127df23c5 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs @@ -0,0 +1,262 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal sealed partial class UnitTestingSolutionCrawlerRegistrationService + { + internal sealed partial class UnitTestingWorkCoordinator + { + private sealed partial class UnitTestingIncrementalAnalyzerProcessor + { + private sealed class UnitTestingLowPriorityProcessor : AbstractUnitTestingPriorityProcessor + { + private readonly UnitTestingAsyncProjectWorkItemQueue _workItemQueue; + + public UnitTestingLowPriorityProcessor( + IAsynchronousOperationListener listener, + UnitTestingIncrementalAnalyzerProcessor processor, + Lazy> lazyAnalyzers, + IGlobalOperationNotificationService globalOperationNotificationService, + TimeSpan backOffTimeSpan, + CancellationToken shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) + { + _workItemQueue = new UnitTestingAsyncProjectWorkItemQueue(processor._registration.ProgressReporter); + + Start(); + } + + public int WorkItemCount => _workItemQueue.WorkItemCount; + + protected override Task WaitAsync(CancellationToken cancellationToken) + => _workItemQueue.WaitAsync(cancellationToken); + + protected override async Task ExecuteAsync() + { + try + { + // we wait for global operation, higher and normal priority processor to finish its working + await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false); + + // process any available project work, preferring the active project. + var preferableProjectId = Processor._documentTracker.SupportsDocumentTracking + ? Processor._documentTracker.TryGetActiveDocument()?.ProjectId + : null; + + if (_workItemQueue.TryTakeAnyWork( + preferableProjectId, +#if false // Not used in unit testing crawling + Processor.DependencyGraph, + Processor.DiagnosticAnalyzerService, +#endif + out var workItem, out var projectCancellation)) + { + await ProcessProjectAsync(Analyzers, workItem, projectCancellation).ConfigureAwait(false); + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable(); + } + } + + protected override Task HigherQueueOperationTask + { + get + { +#if false // Not used in unit testing crawling + return Task.WhenAll(Processor._highPriorityProcessor.Running, Processor._normalPriorityProcessor.Running); +#else + return Processor._normalPriorityProcessor.Running; +#endif + } + } + + protected override bool HigherQueueHasWorkItem + { + get + { + return +#if false // Not used in unit testing crawling + Processor._highPriorityProcessor.HasAnyWork || +#endif + Processor._normalPriorityProcessor.HasAnyWork; + } + } + + protected override void OnPaused() + { + base.OnPaused(); + + _workItemQueue.RequestCancellationOnRunningTasks(); + } + + public void Enqueue(UnitTestingWorkItem item) + { + UpdateLastAccessTime(); + + // Project work + item = item.ToProjectWorkItem(Processor._listener.BeginAsyncOperation("WorkItem")); + + var added = _workItemQueue.AddOrReplace(item); + + // lower priority queue gets lowest time slot possible. if there is any activity going on in higher queue, it drop whatever it has + // and let higher work item run + CancelRunningTaskIfHigherQueueHasWorkItem(); + + Logger.Log(FunctionId.WorkCoordinator_Project_Enqueue, s_enqueueLogger, Environment.TickCount, item.ProjectId, !added); + + UnitTestingSolutionCrawlerLogger.LogWorkItemEnqueue(Processor._logAggregator, item.ProjectId); + } + + private void CancelRunningTaskIfHigherQueueHasWorkItem() + { + if (!HigherQueueHasWorkItem) + { + return; + } + + _workItemQueue.RequestCancellationOnRunningTasks(); + } + + private async Task ProcessProjectAsync(ImmutableArray analyzers, UnitTestingWorkItem workItem, CancellationToken cancellationToken) + { + if (CancellationToken.IsCancellationRequested) + { + return; + } + + // we do have work item for this project + var projectId = workItem.ProjectId; + var processedEverything = false; + var processingSolution = Processor._registration.GetSolutionToAnalyze(); + + try + { + using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessProjectAsync, w => w.ToString(), workItem, cancellationToken)) + { + var project = processingSolution.GetProject(projectId); + if (project != null) + { + var reasons = workItem.InvocationReasons; +#if false // Not used in unit testing crawling + var semanticsChanged = reasons.Contains(UnitTestingPredefinedInvocationReasons.SemanticChanged) || + reasons.Contains(UnitTestingPredefinedInvocationReasons.SolutionRemoved); +#endif + + using (Processor.EnableCaching(project.Id)) + { + await Processor.RunAnalyzersAsync(analyzers, project, workItem, + (a, p, c) => a.AnalyzeProjectAsync(p, +#if false // Not used in unit testing crawling + semanticsChanged, +#endif + reasons, c), cancellationToken).ConfigureAwait(false); + } + } + else + { + UnitTestingSolutionCrawlerLogger.LogProcessProjectNotExist(Processor._logAggregator); + +#if false // Not used in unit testing crawling + await RemoveProjectAsync(projectId, cancellationToken).ConfigureAwait(false); +#endif + } + + if (!cancellationToken.IsCancellationRequested) + { + processedEverything = true; + } + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + // we got cancelled in the middle of processing the project. + // let's make sure newly enqueued work item has all the flag needed. + // Avoid retry attempts after cancellation is requested, since work will not be processed + // after that point. + if (!processedEverything && !CancellationToken.IsCancellationRequested) + { + _workItemQueue.AddOrReplace(workItem.Retry(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); + } + + UnitTestingSolutionCrawlerLogger.LogProcessProject(Processor._logAggregator, projectId.Id, processedEverything); + + // remove one that is finished running + _workItemQueue.MarkWorkItemDoneFor(projectId); + } + } + +#if false // Not used in unit testing crawling + private async Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) + { + foreach (var analyzer in Analyzers) + { + await analyzer.RemoveProjectAsync(projectId, cancellationToken).ConfigureAwait(false); + } + } +#endif + + public override void Shutdown() + { + base.Shutdown(); + _workItemQueue.Dispose(); + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly UnitTestingLowPriorityProcessor _lowPriorityProcessor; + + internal TestAccessor(UnitTestingLowPriorityProcessor lowPriorityProcessor) + { + _lowPriorityProcessor = lowPriorityProcessor; + } + + internal void WaitUntilCompletion(ImmutableArray analyzers, List items) + { + var uniqueIds = new HashSet(); + foreach (var item in items) + { + if (uniqueIds.Add(item.ProjectId)) + { + _lowPriorityProcessor.ProcessProjectAsync(analyzers, item, CancellationToken.None).Wait(); + } + } + } + + internal void WaitUntilCompletion() + { + // this shouldn't happen. would like to get some diagnostic + while (_lowPriorityProcessor._workItemQueue.HasAnyWork) + { + FailFast.Fail("How?"); + } + } + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingNormalPriorityProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingNormalPriorityProcessor.cs new file mode 100644 index 0000000000000..faecdaa5babff --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingNormalPriorityProcessor.cs @@ -0,0 +1,667 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +#if DEBUG +using System.Diagnostics; +#endif + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal sealed partial class UnitTestingSolutionCrawlerRegistrationService + { + internal sealed partial class UnitTestingWorkCoordinator + { + private sealed partial class UnitTestingIncrementalAnalyzerProcessor + { + private sealed class UnitTestingNormalPriorityProcessor : AbstractUnitTestingPriorityProcessor + { + private const int MaxHighPriorityQueueCache = 29; + + private readonly UnitTestingAsyncDocumentWorkItemQueue _workItemQueue; + private readonly ConcurrentDictionary _higherPriorityDocumentsNotProcessed; + + private ProjectId? _currentProjectProcessing; + private IDisposable? _projectCache; + + // this is only used in ResetState to find out solution has changed + // and reset some states such as logging some telemetry or + // priorities active,visible, opened files and etc + private Solution? _lastSolution = null; + + // whether this processor is running or not + private Task _running; + + public UnitTestingNormalPriorityProcessor( + IAsynchronousOperationListener listener, + UnitTestingIncrementalAnalyzerProcessor processor, + Lazy> lazyAnalyzers, + IGlobalOperationNotificationService globalOperationNotificationService, + TimeSpan backOffTimeSpan, + CancellationToken shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) + { + _running = Task.CompletedTask; + _workItemQueue = new UnitTestingAsyncDocumentWorkItemQueue(processor._registration.ProgressReporter); + _higherPriorityDocumentsNotProcessed = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 20); + + _currentProjectProcessing = null; + + Start(); + } + + public void Enqueue(UnitTestingWorkItem item) + { + Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item"); + + UpdateLastAccessTime(); + + var added = _workItemQueue.AddOrReplace(item); + + Logger.Log(FunctionId.WorkCoordinator_DocumentWorker_Enqueue, s_enqueueLogger, Environment.TickCount, item.DocumentId, !added); + + CheckHigherPriorityDocument(item); + + UnitTestingSolutionCrawlerLogger.LogWorkItemEnqueue( + Processor._logAggregator, item.Language, item.DocumentId, item.InvocationReasons, item.IsLowPriority, item.ActiveMember, added); + } + + private void CheckHigherPriorityDocument(UnitTestingWorkItem item) + { + Contract.ThrowIfFalse(item.DocumentId != null); + + if (!item.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.HighPriority)) + { + return; + } + + AddHigherPriorityDocument(item.DocumentId); + } + + private void AddHigherPriorityDocument(DocumentId id) + { + var cache = GetHighPriorityQueueProjectCache(id); + if (!_higherPriorityDocumentsNotProcessed.TryAdd(id, cache)) + { + // we already have the document in the queue. + cache?.Dispose(); + } + + UnitTestingSolutionCrawlerLogger.LogHigherPriority(Processor._logAggregator, id.Id); + } + + private IDisposable? GetHighPriorityQueueProjectCache(DocumentId id) + { + // NOTE: we have one potential issue where we can cache a lot of stuff in memory + // since we will cache all high prioirty work's projects in memory until they are processed. + // + // To mitigate that, we will turn off cache if we have too many items in high priority queue + // this shouldn't affect active file since we always enable active file cache from background compiler. + + return _higherPriorityDocumentsNotProcessed.Count <= MaxHighPriorityQueueCache ? Processor.EnableCaching(id.ProjectId) : null; + } + + protected override Task WaitAsync(CancellationToken cancellationToken) + { + if (!_workItemQueue.HasAnyWork) + { + _projectCache?.Dispose(); + _projectCache = null; + } + + return _workItemQueue.WaitAsync(cancellationToken); + } + + public Task Running => _running; + public int WorkItemCount => _workItemQueue.WorkItemCount; + public bool HasAnyWork => _workItemQueue.HasAnyWork; + + protected override async Task ExecuteAsync() + { + if (CancellationToken.IsCancellationRequested) + { + return; + } + + var source = new TaskCompletionSource(); + try + { + // mark it as running + _running = source.Task; + + await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false); + + // okay, there must be at least one item in the map + ResetStates(); + + if (await TryProcessOneHigherPriorityDocumentAsync().ConfigureAwait(false)) + { + // successfully processed a high priority document. + return; + } + + // process one of documents remaining + if (!_workItemQueue.TryTakeAnyWork( + _currentProjectProcessing, +#if false // Not used in unit testing crawling + Processor.DependencyGraph, + Processor.DiagnosticAnalyzerService, +#endif + out var workItem, + out var documentCancellation)) + { + return; + } + + // check whether we have been shutdown + if (CancellationToken.IsCancellationRequested) + { + return; + } + + // check whether we have moved to new project + SetProjectProcessing(workItem.ProjectId); + + // process the new document + await ProcessDocumentAsync(Analyzers, workItem, documentCancellation).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + // mark it as done running + source.SetResult(null); + } + } + + protected override Task HigherQueueOperationTask +#if false // Not used in unit testing crawling + { + get + { + return Processor._highPriorityProcessor.Running; + } + } +#else + => Task.CompletedTask; +#endif + + protected override bool HigherQueueHasWorkItem +#if false // Not used in unit testing crawling + { + get + { + return Processor._highPriorityProcessor.HasAnyWork; + } + } +#else + => false; +#endif + + protected override void OnPaused() + { + base.OnPaused(); + _workItemQueue.RequestCancellationOnRunningTasks(); + } + + private void SetProjectProcessing(ProjectId currentProject) + { + EnableProjectCacheIfNecessary(currentProject); + + _currentProjectProcessing = currentProject; + } + + private void EnableProjectCacheIfNecessary(ProjectId currentProject) + { + if (_projectCache != null && currentProject == _currentProjectProcessing) + { + return; + } + + _projectCache?.Dispose(); + _projectCache = Processor.EnableCaching(currentProject); + } + + private IEnumerable GetPrioritizedPendingDocuments() + { + // First the active document + var activeDocumentId = Processor._documentTracker.TryGetActiveDocument(); + if (activeDocumentId != null) + { + yield return activeDocumentId; + } + + // Now any visible documents + foreach (var visibleDocumentId in Processor._documentTracker.GetVisibleDocuments()) + { + yield return visibleDocumentId; + } + + // Any other high priority documents + foreach (var (documentId, _) in _higherPriorityDocumentsNotProcessed) + { + yield return documentId; + } + } + + private async Task TryProcessOneHigherPriorityDocumentAsync() + { + try + { + if (!Processor._documentTracker.SupportsDocumentTracking) + { + return false; + } + + foreach (var documentId in GetPrioritizedPendingDocuments()) + { + if (CancellationToken.IsCancellationRequested) + { + return true; + } + + // this is a best effort algorithm with some shortcomings. + // + // the most obvious issue is if there is a new work item (without a solution change - but very unlikely) + // for a opened document we already processed, the work item will be treated as a regular one rather than higher priority one + // (opened document) + // see whether we have work item for the document + if (!_workItemQueue.TryTake(documentId, out var workItem, out var documentCancellation)) + { + RemoveHigherPriorityDocument(documentId); + continue; + } + + // okay now we have work to do + await ProcessDocumentAsync(Analyzers, workItem, documentCancellation).ConfigureAwait(false); + + RemoveHigherPriorityDocument(documentId); + return true; + } + + return false; + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable(); + } + } + + private void RemoveHigherPriorityDocument(DocumentId documentId) + { + // remove opened document processed + if (_higherPriorityDocumentsNotProcessed.TryRemove(documentId, out var projectCache)) + { + projectCache?.Dispose(); + } + } + + private async Task ProcessDocumentAsync(ImmutableArray analyzers, UnitTestingWorkItem workItem, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(workItem.DocumentId); + + if (CancellationToken.IsCancellationRequested) + { + return; + } + + var processedEverything = false; + var documentId = workItem.DocumentId; + + // we should always use solution snapshot after workitem is removed from the queue. + // otherwise, we can have a race such as below. + // + // 1.solution crawler picked up a solution + // 2.before processing the solution, an workitem got changed + // 3.and then the work item got picked up from the queue + // 4.and use the work item with the solution that got picked up in step 1 + // + // step 2 is happening because solution has changed, but step 4 used old solution from step 1 + // that doesn't have effects of the solution changes. + // + // solution crawler must remove the work item from the queue first and then pick up the soluton, + // so that the queue gets new work item if there is any solution changes after the work item is removed + // from the queue + // + // using later version of solution is always fine since, as long as there is new work item in the queue, + // solution crawler will eventually call the last workitem with the lastest solution + // making everything to catch up + var solution = Processor._registration.GetSolutionToAnalyze(); + try + { + using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken)) + { + var textDocument = solution.GetTextDocument(documentId) ?? await solution.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); + + if (textDocument != null) + { +#if false // Not used in unit testing crawling + // if we are called because a document is opened, we invalidate the document so that + // it can be re-analyzed. otherwise, since newly opened document has same version as before + // analyzer will simply return same data back + if (workItem.MustRefresh && !workItem.IsRetry) + { + var isOpen = textDocument.IsOpen(); + + await ProcessOpenDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false); + await ProcessCloseDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false); + } +#endif + + // check whether we are having special reanalyze request + await ProcessReanalyzeDocumentAsync(workItem, textDocument, cancellationToken).ConfigureAwait(false); + + await Processor.ProcessDocumentAnalyzersAsync(textDocument, analyzers, workItem, cancellationToken).ConfigureAwait(false); + } + else + { + UnitTestingSolutionCrawlerLogger.LogProcessDocumentNotExist(Processor._logAggregator); + + await RemoveDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); + } + + if (!cancellationToken.IsCancellationRequested) + { + processedEverything = true; + } + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + // we got cancelled in the middle of processing the document. + // let's make sure newly enqueued work item has all the flag needed. + // Avoid retry attempts after cancellation is requested, since work will not be processed + // after that point. + if (!processedEverything && !CancellationToken.IsCancellationRequested) + { + _workItemQueue.AddOrReplace(workItem.Retry(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); + } + + UnitTestingSolutionCrawlerLogger.LogProcessDocument(Processor._logAggregator, documentId.Id, processedEverything); + + // remove one that is finished running + _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId); + } + } + +#if false // Not used in unit testing crawling + private async Task ProcessOpenDocumentIfNeededAsync(ImmutableArray analyzers, UnitTestingWorkItem workItem, TextDocument textDocument, bool isOpen, CancellationToken cancellationToken) + { + if (!isOpen || !workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentOpened)) + { + return; + } + + UnitTestingSolutionCrawlerLogger.LogProcessOpenDocument(Processor._logAggregator, textDocument.Id.Id); + + await Processor.RunAnalyzersAsync(analyzers, textDocument, workItem, DocumentOpenAsync, cancellationToken).ConfigureAwait(false); + return; + + static async Task DocumentOpenAsync(IUnitTestingIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) + { + if (textDocument is Document document) + { + await analyzer.DocumentOpenAsync(document, cancellationToken).ConfigureAwait(false); + } + else + { + await analyzer.NonSourceDocumentOpenAsync(textDocument, cancellationToken).ConfigureAwait(false); + } + } + } + + private async Task ProcessCloseDocumentIfNeededAsync(ImmutableArray analyzers, UnitTestingWorkItem workItem, TextDocument textDocument, bool isOpen, CancellationToken cancellationToken) + { + if (isOpen || !workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentClosed)) + { + return; + } + + UnitTestingSolutionCrawlerLogger.LogProcessCloseDocument(Processor._logAggregator, textDocument.Id.Id); + + await Processor.RunAnalyzersAsync(analyzers, textDocument, workItem, DocumentCloseAsync, cancellationToken).ConfigureAwait(false); + return; + + static async Task DocumentCloseAsync(IUnitTestingIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) + { + if (textDocument is Document document) + { + await analyzer.DocumentCloseAsync(document, cancellationToken).ConfigureAwait(false); + } + else + { + await analyzer.NonSourceDocumentCloseAsync(textDocument, cancellationToken).ConfigureAwait(false); + } + } + } +#endif + + private async Task ProcessReanalyzeDocumentAsync(UnitTestingWorkItem workItem, TextDocument document, CancellationToken cancellationToken) + { + try + { +#if DEBUG + Debug.Assert(!workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.Reanalyze) || workItem.SpecificAnalyzers.Count > 0); +#endif + + // No-reanalyze request or we already have a request to re-analyze every thing + if ( +#if false // Not used in unit testing crawling + workItem.MustRefresh || +#endif + !workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.Reanalyze)) + { + return; + } + + // First reset the document state in analyzers. + var reanalyzers = workItem.SpecificAnalyzers.ToImmutableArray(); +#if false // Not used in unit testing crawling + await Processor.RunAnalyzersAsync(reanalyzers, document, workItem, DocumentResetAsync, cancellationToken).ConfigureAwait(false); +#endif + + // No request to re-run syntax change analysis. run it here + var reasons = workItem.InvocationReasons; +#if false // Not used in unit testing crawling + if (!reasons.Contains(UnitTestingPredefinedInvocationReasons.SyntaxChanged)) + { + await Processor.RunAnalyzersAsync(reanalyzers, document, workItem, (a, d, c) => AnalyzeSyntaxAsync(a, d, reasons, c), cancellationToken).ConfigureAwait(false); + } +#endif + + // No request to re-run semantic change analysis. run it here + // Note: Semantic analysis is not supported for non-source documents. + if (document is Document sourceDocument && + !workItem.InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.SemanticChanged)) + { + await Processor.RunAnalyzersAsync(reanalyzers, sourceDocument, workItem, + (a, d, c) => a.AnalyzeDocumentAsync( + d, +#if false // Not used in unit testing crawling + bodyOpt: null, +#endif + reasons, + c), cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + + return; + +#if false // Not used in unit testing crawling + static async Task DocumentResetAsync(IUnitTestingIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) + { + if (textDocument is Document document) + { + await analyzer.DocumentResetAsync(document, cancellationToken).ConfigureAwait(false); + } + else + { + await analyzer.NonSourceDocumentResetAsync(textDocument, cancellationToken).ConfigureAwait(false); + } + } +#endif + +#if false // Not used in unit testing crawling + static async Task AnalyzeSyntaxAsync(IUnitTestingIncrementalAnalyzer analyzer, TextDocument textDocument, UnitTestingInvocationReasons reasons, CancellationToken cancellationToken) + { + if (textDocument is Document document) + { + await analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken).ConfigureAwait(false); + } + else + { + await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, reasons, cancellationToken).ConfigureAwait(false); + } + } +#endif + } + + private Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) + => RemoveDocumentAsync(Analyzers, documentId, cancellationToken); + + private static async Task RemoveDocumentAsync(ImmutableArray analyzers, DocumentId documentId, CancellationToken cancellationToken) + { + foreach (var analyzer in analyzers) + { + await analyzer.RemoveDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); + } + } + + private void ResetStates() + { + try + { + if (!IsSolutionChanged()) + { + return; + } + +#if false // Not used in unit testing crawling + await Processor.RunAnalyzersAsync( + Analyzers, + Processor._registration.GetSolutionToAnalyze(), + workItem: new UnitTestingWorkItem(), (a, s, c) => a.NewSolutionSnapshotAsync(s, c), CancellationToken).ConfigureAwait(false); +#endif + +#if false // Not used in unit testing crawling + foreach (var id in Processor.GetOpenDocumentIds()) + { + AddHigherPriorityDocument(id); + } +#endif + + UnitTestingSolutionCrawlerLogger.LogResetStates(Processor._logAggregator); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) + { + throw ExceptionUtilities.Unreachable(); + } + + return; + + bool IsSolutionChanged() + { + var currentSolution = Processor._registration.GetSolutionToAnalyze(); + var oldSolution = _lastSolution; + + if (currentSolution == oldSolution) + { + return false; + } + + _lastSolution = currentSolution; + + ResetLogAggregatorIfNeeded(currentSolution, oldSolution); + + return true; + } + + void ResetLogAggregatorIfNeeded(Solution currentSolution, Solution? oldSolution) + { + if (oldSolution == null || currentSolution.Id == oldSolution.Id) + { + // we log aggregated info when solution is changed such as + // new solution is opened or solution is closed + return; + } + + // this log things like how many time we analyzed active files, how many times other files are analyzed, + // avg time to analyze files, how many solution snapshot got analyzed and etc. + // all accumultation is done in VS side and we only send statistics to VS telemetry otherwise, it is too much + // data to send + UnitTestingSolutionCrawlerLogger.LogIncrementalAnalyzerProcessorStatistics( + Processor._registration.CorrelationId, oldSolution, Processor._logAggregator, Analyzers); + + Processor.ResetLogAggregator(); + } + } + + public override void Shutdown() + { + base.Shutdown(); + + _workItemQueue.Dispose(); + + _projectCache?.Dispose(); + _projectCache = null; + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly UnitTestingNormalPriorityProcessor _normalPriorityProcessor; + + internal TestAccessor(UnitTestingNormalPriorityProcessor normalPriorityProcessor) + { + _normalPriorityProcessor = normalPriorityProcessor; + } + + internal void WaitUntilCompletion(ImmutableArray analyzers, List items) + { + foreach (var item in items) + { + _normalPriorityProcessor.ProcessDocumentAsync(analyzers, item, CancellationToken.None).Wait(); + } + } + + internal void WaitUntilCompletion() + { + // this shouldn't happen. would like to get some diagnostic + while (_normalPriorityProcessor._workItemQueue.HasAnyWork) + { + FailFast.Fail("How?"); + } + } + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingSemanticChangeProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingSemanticChangeProcessor.cs new file mode 100644 index 0000000000000..0a22ea8d2d2f6 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingSemanticChangeProcessor.cs @@ -0,0 +1,464 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal sealed partial class UnitTestingSolutionCrawlerRegistrationService + { + /// + /// this will be used in the unit test to indicate certain action has happened or not. + /// + public const string EnqueueItem = nameof(EnqueueItem); + + internal sealed partial class UnitTestingWorkCoordinator + { + private sealed class UnitTestingSemanticChangeProcessor : UnitTestingIdleProcessor + { + private static readonly Func s_enqueueLogger = (tick, documentId, hint) => $"Tick:{tick}, {documentId}, {documentId.ProjectId}, hint:{hint}"; + + private readonly SemaphoreSlim _gate; + + private readonly UnitTestingRegistration _registration; + private readonly UnitTestingProjectProcessor _processor; + + private readonly NonReentrantLock _workGate = new(); + private readonly Dictionary _pendingWork = new(); + + public UnitTestingSemanticChangeProcessor( + IAsynchronousOperationListener listener, + UnitTestingRegistration registration, + UnitTestingIncrementalAnalyzerProcessor documentWorkerProcessor, + TimeSpan backOffTimeSpan, + TimeSpan projectBackOffTimeSpan, + CancellationToken cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) + { + _gate = new SemaphoreSlim(initialCount: 0); + + _registration = registration; + + _processor = new UnitTestingProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpan, cancellationToken); + + Start(); + + // Register a clean-up task to ensure pending work items are flushed from the queue if they will + // never be processed. + AsyncProcessorTask.ContinueWith( + _ => ClearQueueWorker(_workGate, _pendingWork, data => data.AsyncToken), + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + protected override void OnPaused() + { + } + + public override Task AsyncProcessorTask + => Task.WhenAll(base.AsyncProcessorTask, _processor.AsyncProcessorTask); + + protected override Task WaitAsync(CancellationToken cancellationToken) + => _gate.WaitAsync(cancellationToken); + + protected override async Task ExecuteAsync() + { + var data = Dequeue(); + + using (data.AsyncToken) + { + // we have a hint. check whether we can take advantage of it + if (await TryEnqueueFromHintAsync(data).ConfigureAwait(continueOnCapturedContext: false)) + return; + + EnqueueFullProjectDependency(data.Project); + } + } + + private UnitTestingData Dequeue() + => DequeueWorker(_workGate, _pendingWork, CancellationToken); + + private async Task TryEnqueueFromHintAsync(UnitTestingData data) + { + var changedMember = data.ChangedMember; + if (changedMember == null) + return false; + + var document = data.GetRequiredDocument(); + + // see whether we already have semantic model. otherwise, use the expansive full project dependency one + // TODO: if there is a reliable way to track changed member, we could use GetSemanticModel here which could + // rebuild compilation from scratch + if (!document.TryGetSemanticModel(out var model) || + !changedMember.TryResolve(await document.GetSyntaxRootAsync(CancellationToken).ConfigureAwait(false), out SyntaxNode? declarationNode)) + { + return false; + } + + var symbol = model.GetDeclaredSymbol(declarationNode, CancellationToken); + if (symbol == null) + { + return false; + } + + return await TryEnqueueFromMemberAsync(document, symbol).ConfigureAwait(false) || + await TryEnqueueFromTypeAsync(document, symbol).ConfigureAwait(false); + } + + private async Task TryEnqueueFromTypeAsync(Document document, ISymbol symbol) + { + if (!IsType(symbol)) + { + return false; + } + + if (symbol.DeclaredAccessibility == Accessibility.Private) + { + await EnqueueWorkItemAsync(document, symbol).ConfigureAwait(false); + + Logger.Log(FunctionId.WorkCoordinator_SemanticChange_EnqueueFromType, symbol.Name); + return true; + } + + if (IsInternal(symbol)) + { + var assembly = symbol.ContainingAssembly; + EnqueueFullProjectDependency(document.Project, assembly); + return true; + } + + return false; + } + + private async Task TryEnqueueFromMemberAsync(Document document, ISymbol symbol) + { + if (!IsMember(symbol)) + { + return false; + } + + var typeSymbol = symbol.ContainingType; + + if (symbol.DeclaredAccessibility == Accessibility.Private) + { + await EnqueueWorkItemAsync(document, symbol).ConfigureAwait(false); + + Logger.Log(FunctionId.WorkCoordinator_SemanticChange_EnqueueFromMember, symbol.Name); + return true; + } + + if (typeSymbol == null) + { + return false; + } + + return await TryEnqueueFromTypeAsync(document, typeSymbol).ConfigureAwait(false); + } + + private Task EnqueueWorkItemAsync(Document document, ISymbol symbol) + => EnqueueWorkItemAsync(document, symbol.ContainingType != null ? symbol.ContainingType.Locations : symbol.Locations); + + private async Task EnqueueWorkItemAsync(Document thisDocument, ImmutableArray locations) + { + var solution = thisDocument.Project.Solution; + var projectId = thisDocument.Id.ProjectId; + + foreach (var location in locations) + { + Debug.Assert(location.IsInSource); + + var documentId = solution.GetDocumentId(location.SourceTree, projectId); + if (documentId == null || thisDocument.Id == documentId) + continue; + + await _processor.EnqueueWorkItemAsync(solution.GetRequiredProject(documentId.ProjectId), documentId, document: null).ConfigureAwait(false); + } + } + + private static bool IsInternal(ISymbol symbol) + { + return symbol.DeclaredAccessibility is Accessibility.Internal or + Accessibility.ProtectedAndInternal or + Accessibility.ProtectedOrInternal; + } + + private static bool IsType(ISymbol symbol) + => symbol.Kind == SymbolKind.NamedType; + + private static bool IsMember(ISymbol symbol) + { + return symbol.Kind is SymbolKind.Event or + SymbolKind.Field or + SymbolKind.Method or + SymbolKind.Property; + } + + private void EnqueueFullProjectDependency(Project project, IAssemblySymbol? internalVisibleToAssembly = null) + { + var self = project.Id; + + // if there is no hint (this can happen for cases such as solution/project load and etc), + // we can postpone it even further + if (internalVisibleToAssembly == null) + { + _processor.Enqueue(self, needDependencyTracking: true); + return; + } + + // most likely we got here since we are called due to typing. + // calculate dependency here and register each affected project to the next pipe line + var solution = project.Solution; + foreach (var projectId in GetProjectsToAnalyze(solution, self)) + { + var otherProject = solution.GetProject(projectId); + if (otherProject == null) + continue; + + if (otherProject.TryGetCompilation(out var compilation)) + { + var assembly = compilation.Assembly; + if (assembly != null && !assembly.IsSameAssemblyOrHasFriendAccessTo(internalVisibleToAssembly)) + continue; + } + + _processor.Enqueue(projectId); + } + + Logger.Log(FunctionId.WorkCoordinator_SemanticChange_FullProjects, internalVisibleToAssembly == null ? "full" : "internals"); + } + + public void Enqueue(Project project, DocumentId documentId, Document? document, SyntaxPath? changedMember) + { + UpdateLastAccessTime(); + + using (_workGate.DisposableWait(CancellationToken)) + { + if (_pendingWork.TryGetValue(documentId, out var data)) + { + // create new async token and dispose old one. + var newAsyncToken = Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Services); + data.AsyncToken.Dispose(); + + _pendingWork[documentId] = new UnitTestingData(project, documentId, document, data.ChangedMember == changedMember ? changedMember : null, newAsyncToken); + return; + } + + _pendingWork.Add(documentId, new UnitTestingData(project, documentId, document, changedMember, Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Services))); + _gate.Release(); + } + + Logger.Log(FunctionId.WorkCoordinator_SemanticChange_Enqueue, s_enqueueLogger, Environment.TickCount, documentId, changedMember != null); + } + + private static TValue DequeueWorker(NonReentrantLock gate, Dictionary map, CancellationToken cancellationToken) + where TKey : notnull + { + using (gate.DisposableWait(cancellationToken)) + { + var first = default(KeyValuePair); + foreach (var kv in map) + { + first = kv; + break; + } + + // this is only one that removes data from the queue. so, it should always succeed + var result = map.Remove(first.Key); + Debug.Assert(result); + + return first.Value; + } + } + + private static void ClearQueueWorker(NonReentrantLock gate, Dictionary map, Func disposerSelector) + where TKey : notnull + { + using (gate.DisposableWait(CancellationToken.None)) + { + foreach (var (_, data) in map) + { + disposerSelector?.Invoke(data)?.Dispose(); + } + + map.Clear(); + } + } + + private static IEnumerable GetProjectsToAnalyze(Solution solution, ProjectId projectId) + { + var graph = solution.GetProjectDependencyGraph(); + + // Reanalyze direct dependencies only as reanalyzing all transitive dependencies is very expensive. + return graph.GetProjectsThatDirectlyDependOnThisProject(projectId).Concat(projectId); + } + + private readonly struct UnitTestingData + { + private readonly DocumentId _documentId; + private readonly Document? _document; + + public readonly Project Project; + public readonly SyntaxPath? ChangedMember; + public readonly IAsyncToken AsyncToken; + + public UnitTestingData(Project project, DocumentId documentId, Document? document, SyntaxPath? changedMember, IAsyncToken asyncToken) + { + _documentId = documentId; + _document = document; + Project = project; + ChangedMember = changedMember; + AsyncToken = asyncToken; + } + + public Document GetRequiredDocument() + => UnitTestingWorkCoordinator.GetRequiredDocument(Project, _documentId, _document); + } + + private class UnitTestingProjectProcessor : UnitTestingIdleProcessor + { + private static readonly Func s_enqueueLogger = (t, i) => string.Format("[{0}] {1}", t, i.ToString()); + + private readonly SemaphoreSlim _gate; + + private readonly UnitTestingRegistration _registration; + private readonly UnitTestingIncrementalAnalyzerProcessor _processor; + + private readonly NonReentrantLock _workGate = new(); + private readonly Dictionary _pendingWork = new(); + + public UnitTestingProjectProcessor( + IAsynchronousOperationListener listener, + UnitTestingRegistration registration, + UnitTestingIncrementalAnalyzerProcessor processor, + TimeSpan backOffTimeSpan, + CancellationToken cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) + { + _registration = registration; + _processor = processor; + + _gate = new SemaphoreSlim(initialCount: 0); + + Start(); + + // Register a clean-up task to ensure pending work items are flushed from the queue if they will + // never be processed. + AsyncProcessorTask.ContinueWith( + _ => ClearQueueWorker(_workGate, _pendingWork, data => data.AsyncToken), + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + protected override void OnPaused() + { + } + + public void Enqueue(ProjectId projectId, bool needDependencyTracking = false) + { + UpdateLastAccessTime(); + + using (_workGate.DisposableWait(CancellationToken)) + { + // the project is already in the queue. nothing needs to be done + if (_pendingWork.ContainsKey(projectId)) + { + return; + } + + var data = new UnitTestingData(projectId, needDependencyTracking, Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Services)); + + _pendingWork.Add(projectId, data); + _gate.Release(); + } + + Logger.Log(FunctionId.WorkCoordinator_Project_Enqueue, s_enqueueLogger, Environment.TickCount, projectId); + } + + public async Task EnqueueWorkItemAsync(Project project, DocumentId documentId, Document? document) + { + // we are shutting down + CancellationToken.ThrowIfCancellationRequested(); + + // call to this method is serialized. and only this method does the writing. + var priorityService = project.GetLanguageService(); + var isLowPriority = priorityService != null && await priorityService.IsLowPriorityAsync(GetRequiredDocument(project, documentId, document), CancellationToken).ConfigureAwait(false); + + _processor.Enqueue( + new UnitTestingWorkItem(documentId, project.Language, UnitTestingInvocationReasons.SemanticChanged, + isLowPriority, activeMember: null, Listener.BeginAsyncOperation(nameof(EnqueueWorkItemAsync), tag: EnqueueItem))); + } + + protected override Task WaitAsync(CancellationToken cancellationToken) + => _gate.WaitAsync(cancellationToken); + + protected override async Task ExecuteAsync() + { + var data = Dequeue(); + + using (data.AsyncToken) + { + var project = _registration.GetSolutionToAnalyze().GetProject(data.ProjectId); + if (project == null) + { + return; + } + + if (!data.NeedDependencyTracking) + { + await EnqueueWorkItemAsync(project).ConfigureAwait(false); + return; + } + + // do dependency tracking here with current solution + var solution = _registration.GetSolutionToAnalyze(); + foreach (var projectId in GetProjectsToAnalyze(solution, data.ProjectId)) + { + project = solution.GetProject(projectId); + await EnqueueWorkItemAsync(project).ConfigureAwait(false); + } + } + } + + private UnitTestingData Dequeue() + => DequeueWorker(_workGate, _pendingWork, CancellationToken); + + private async Task EnqueueWorkItemAsync(Project? project) + { + if (project == null) + return; + + foreach (var documentId in project.DocumentIds) + await EnqueueWorkItemAsync(project, documentId, document: null).ConfigureAwait(false); + } + + private readonly struct UnitTestingData + { + public readonly IAsyncToken AsyncToken; + public readonly ProjectId ProjectId; + public readonly bool NeedDependencyTracking; + + public UnitTestingData(ProjectId projectId, bool needDependencyTracking, IAsyncToken asyncToken) + { + AsyncToken = asyncToken; + ProjectId = projectId; + NeedDependencyTracking = needDependencyTracking; + } + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingWorkItem.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingWorkItem.cs new file mode 100644 index 0000000000000..d4aa02c86b355 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingWorkItem.cs @@ -0,0 +1,183 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal partial class UnitTestingWorkCoordinator + { + // this is internal only type + private readonly struct UnitTestingWorkItem + { + // project related workitem + public readonly ProjectId ProjectId; + + // document related workitem + public readonly DocumentId? DocumentId; + public readonly string Language; + public readonly UnitTestingInvocationReasons InvocationReasons; + public readonly bool IsLowPriority; + + // extra info + public readonly SyntaxPath? ActiveMember; + + /// + /// Non-empty if this work item is intended to be executed only for specific incremental analyzer(s). + /// Otherwise, the work item is applicable to all relevant incremental analyzers. + /// + public readonly ImmutableHashSet SpecificAnalyzers; + + /// + /// Gets all the applicable analyzers to execute for this work item. + /// If this work item has any specific analyzer(s), then returns the intersection of + /// and the given . + /// Otherwise, returns . + /// + public IEnumerable GetApplicableAnalyzers(ImmutableArray allAnalyzers) + => SpecificAnalyzers?.Count > 0 ? SpecificAnalyzers.Where(allAnalyzers.Contains) : allAnalyzers; + + // retry + public readonly bool IsRetry; + + // common + public readonly IAsyncToken AsyncToken; + +#if false // Not used in unit testing crawling + public bool MustRefresh + { + get + { + // in current design, we need to re-run all incremental analyzer on document open and close + // so that incremental analyzer who only cares about opened document can have a chance to clean up + // its state. + return InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentOpened) || + InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentClosed); + } + } +#endif + + private UnitTestingWorkItem( + DocumentId? documentId, + ProjectId projectId, + string language, + UnitTestingInvocationReasons invocationReasons, + bool isLowPriority, + SyntaxPath? activeMember, + ImmutableHashSet specificAnalyzers, + bool retry, + IAsyncToken asyncToken) + { + Debug.Assert(documentId == null || documentId.ProjectId == projectId); + + DocumentId = documentId; + ProjectId = projectId; + Language = language; + InvocationReasons = invocationReasons; + IsLowPriority = isLowPriority; + + ActiveMember = activeMember; + SpecificAnalyzers = specificAnalyzers; + + IsRetry = retry; + + AsyncToken = asyncToken; + } + + public UnitTestingWorkItem(DocumentId documentId, string language, UnitTestingInvocationReasons invocationReasons, bool isLowPriority, SyntaxPath? activeMember, IAsyncToken asyncToken) + : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember, ImmutableHashSet.Create(), retry: false, asyncToken) + { + } + + public UnitTestingWorkItem(DocumentId documentId, string language, UnitTestingInvocationReasons invocationReasons, bool isLowPriority, IUnitTestingIncrementalAnalyzer? analyzer, IAsyncToken asyncToken) + : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember: null, + analyzer == null ? ImmutableHashSet.Create() : ImmutableHashSet.Create(analyzer), + retry: false, asyncToken) + { + } + + public object Key => DocumentId ?? (object)ProjectId; + + private ImmutableHashSet Union(ImmutableHashSet analyzers) + { + if (analyzers.IsEmpty) + { + return SpecificAnalyzers; + } + + if (SpecificAnalyzers.IsEmpty) + { + return analyzers; + } + + return SpecificAnalyzers.Union(analyzers); + } + + public UnitTestingWorkItem Retry(IAsyncToken asyncToken) + { + return new UnitTestingWorkItem( + DocumentId, ProjectId, Language, InvocationReasons, IsLowPriority, ActiveMember, SpecificAnalyzers, + retry: true, asyncToken: asyncToken); + } + + public UnitTestingWorkItem With( + UnitTestingInvocationReasons invocationReasons, SyntaxPath? currentMember, + ImmutableHashSet analyzers, bool retry, IAsyncToken asyncToken) + { + // dispose old one + AsyncToken.Dispose(); + + // create new work item + return new UnitTestingWorkItem( + DocumentId, ProjectId, Language, + InvocationReasons.With(invocationReasons), + IsLowPriority, + ActiveMember == currentMember ? currentMember : null, + Union(analyzers), IsRetry || retry, + asyncToken); + } + + public UnitTestingWorkItem WithAsyncToken(IAsyncToken asyncToken) + { + return new UnitTestingWorkItem( + DocumentId, ProjectId, Language, InvocationReasons, IsLowPriority, ActiveMember, SpecificAnalyzers, + retry: false, asyncToken: asyncToken); + } + + public UnitTestingWorkItem ToProjectWorkItem(IAsyncToken asyncToken) + { + RoslynDebug.Assert(DocumentId != null); + + // create new work item that represents work per project + return new UnitTestingWorkItem( + documentId: null, + DocumentId.ProjectId, + Language, + InvocationReasons, + IsLowPriority, + ActiveMember, + SpecificAnalyzers, + IsRetry, + asyncToken); + } + + public UnitTestingWorkItem With(ImmutableHashSet specificAnalyzers, IAsyncToken asyncToken) + { + return new UnitTestingWorkItem(DocumentId, ProjectId, Language, InvocationReasons, + IsLowPriority, ActiveMember, specificAnalyzers, IsRetry, asyncToken); + } + + public override string ToString() + => $"{DocumentId?.ToString() ?? ProjectId.ToString()}, ({InvocationReasons}), LowPriority:{IsLowPriority}, ActiveMember:{ActiveMember != null}, Retry:{IsRetry}, ({string.Join("|", SpecificAnalyzers.Select(a => a.GetType().Name))})"; + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs new file mode 100644 index 0000000000000..460442ef88766 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs @@ -0,0 +1,829 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler +{ + internal partial class UnitTestingSolutionCrawlerRegistrationService + { + internal sealed partial class UnitTestingWorkCoordinator : IUnitTestingWorkCoordinator + { +#if false // Not used in unit testing crawling + private readonly object _gate = new(); + private readonly IUnitTestingDocumentTrackingService _documentTrackingService; +#endif + + private readonly UnitTestingRegistration _registration; + + private readonly CountLogAggregator _logAggregator = new(); + private readonly IAsynchronousOperationListener _listener; + private readonly IWorkspaceConfigurationService? _workspaceConfigurationService; + + private readonly CancellationTokenSource _shutdownNotificationSource = new(); + private readonly CancellationToken _shutdownToken; + private readonly TaskQueue _eventProcessingQueue; + + // points to processor task + private readonly UnitTestingIncrementalAnalyzerProcessor _documentAndProjectWorkerProcessor; + private readonly UnitTestingSemanticChangeProcessor _semanticChangeProcessor; + + public UnitTestingWorkCoordinator( + IAsynchronousOperationListener listener, + IEnumerable> analyzerProviders, +#if false // Not used in unit testing crawling + bool initializeLazily, +#endif + UnitTestingRegistration registration) + { + _registration = registration; + + _listener = listener; +#if false // Not used in unit testing crawling + _documentTrackingService = _registration.Services.GetRequiredService(); +#endif + _workspaceConfigurationService = _registration.Services.GetService(); + + // event and worker queues + _shutdownToken = _shutdownNotificationSource.Token; + + _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); + + var activeFileBackOffTimeSpan = UnitTestingSolutionCrawlerTimeSpan.ActiveFileWorkerBackOff; + var allFilesWorkerBackOffTimeSpan = UnitTestingSolutionCrawlerTimeSpan.AllFilesWorkerBackOff; + var entireProjectWorkerBackOffTimeSpan = UnitTestingSolutionCrawlerTimeSpan.EntireProjectWorkerBackOff; + + _documentAndProjectWorkerProcessor = new UnitTestingIncrementalAnalyzerProcessor( + listener, + analyzerProviders, +#if false // Not used in unit testing crawling + initializeLazily, +#endif + _registration, + activeFileBackOffTimeSpan, + allFilesWorkerBackOffTimeSpan, + entireProjectWorkerBackOffTimeSpan, + _shutdownToken); + + var semanticBackOffTimeSpan = UnitTestingSolutionCrawlerTimeSpan.SemanticChangeBackOff; + var projectBackOffTimeSpan = UnitTestingSolutionCrawlerTimeSpan.ProjectPropagationBackOff; + + _semanticChangeProcessor = new UnitTestingSemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); + +#if false // Not used in unit testing crawling + // subscribe to active document changed event for active file background analysis scope. + _documentTrackingService.ActiveDocumentChanged += OnActiveDocumentSwitched; +#endif + } + + public UnitTestingRegistration Registration => _registration; + public int CorrelationId => _registration.CorrelationId; + + public void AddAnalyzer( + IUnitTestingIncrementalAnalyzer analyzer +#if false // Not used in unit testing crawling + , bool highPriorityForActiveFile +#endif + ) + { + // add analyzer + _documentAndProjectWorkerProcessor.AddAnalyzer( + analyzer +#if false // Not used in unit testing crawling + , highPriorityForActiveFile +#endif + ); + + // and ask to re-analyze whole solution for the given analyzer + var scope = new UnitTestingReanalyzeScope(_registration.GetSolutionToAnalyze().Id); + Reanalyze(analyzer, scope); + } + +#if false // Not used in unit testing crawling + public void Shutdown(bool blockingShutdown) + { + _documentTrackingService.ActiveDocumentChanged -= OnActiveDocumentSwitched; + + // detach from the workspace + _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; + _registration.Workspace.TextDocumentOpened -= OnTextDocumentOpened; + _registration.Workspace.TextDocumentClosed -= OnTextDocumentClosed; + + // cancel any pending blocks + _shutdownNotificationSource.Cancel(); + + _documentAndProjectWorkerProcessor.Shutdown(); + + UnitTestingSolutionCrawlerLogger.LogWorkCoordinatorShutdown(CorrelationId, _logAggregator); + + if (blockingShutdown) + { + var shutdownTask = Task.WhenAll( + _eventProcessingQueue.LastScheduledTask, + _documentAndProjectWorkerProcessor.AsyncProcessorTask, + _semanticChangeProcessor.AsyncProcessorTask); + + try + { + shutdownTask.Wait(TimeSpan.FromSeconds(5)); + } + catch (AggregateException ex) + { + ex.Handle(e => e is OperationCanceledException); + } + + if (!shutdownTask.IsCompleted) + { + UnitTestingSolutionCrawlerLogger.LogWorkCoordinatorShutdownTimeout(CorrelationId); + } + } + + foreach (var analyzer in _documentAndProjectWorkerProcessor.Analyzers) + { + (analyzer as IDisposable)?.Dispose(); + } + } +#endif + + public void Reanalyze(IUnitTestingIncrementalAnalyzer analyzer, UnitTestingReanalyzeScope scope +#if false // Not used in unit testing crawling + , bool highPriority = false +#endif + ) + { + _eventProcessingQueue.ScheduleTask("Reanalyze", + () => EnqueueWorkItemAsync(analyzer, scope +#if false // Not used in unit testing crawling + , highPriority +#endif + ), _shutdownToken); + + if (scope.HasMultipleDocuments) + { + // log big reanalysis request from things like fix all, suppress all or option changes + // we are not interested in 1 file re-analysis request which can happen from like venus typing + var solution = _registration.GetSolutionToAnalyze(); + UnitTestingSolutionCrawlerLogger.LogReanalyze( + CorrelationId, analyzer, scope.GetDocumentCount(solution), scope.GetLanguagesStringForTelemetry(solution) +#if false // Not used in unit testing crawling + , highPriority +#endif + ); + } + } + +#if false // Not used in unit testing crawling + private void OnActiveDocumentSwitched(object? sender, DocumentId? activeDocumentId) + { + if (activeDocumentId == null) + return; + + var solution = _registration.GetSolutionToAnalyze(); + EnqueueFullDocumentEvent(solution, activeDocumentId, UnitTestingInvocationReasons.ActiveDocumentSwitched, eventName: nameof(OnActiveDocumentSwitched)); + } +#endif + + public void OnWorkspaceChanged(WorkspaceChangeEventArgs args) + { + // guard us from cancellation + try + { + ProcessEvent(args, "OnWorkspaceChanged"); + } + catch (OperationCanceledException oce) + { + if (NotOurShutdownToken(oce)) + { + throw; + } + + // it is our cancellation, ignore + } + catch (AggregateException ae) + { + ae = ae.Flatten(); + + // If we had a mix of exceptions, don't eat it + if (ae.InnerExceptions.Any(e => e is not OperationCanceledException) || + ae.InnerExceptions.Cast().Any(NotOurShutdownToken)) + { + // We had a cancellation with a different token, so don't eat it + throw; + } + + // it is our cancellation, ignore + } + } + + private bool NotOurShutdownToken(OperationCanceledException oce) + => oce.CancellationToken == _shutdownToken; + + private void ProcessEvent(WorkspaceChangeEventArgs args, string eventName) + { + UnitTestingSolutionCrawlerLogger.LogWorkspaceEvent(_logAggregator, args.Kind); + + // TODO: add telemetry that record how much it takes to process an event (max, min, average and etc) + switch (args.Kind) + { + case WorkspaceChangeKind.SolutionAdded: + EnqueueFullSolutionEvent(args.NewSolution, UnitTestingInvocationReasons.DocumentAdded, eventName); + break; + + case WorkspaceChangeKind.SolutionChanged: + case WorkspaceChangeKind.SolutionReloaded: + EnqueueSolutionChangedEvent(args.OldSolution, args.NewSolution, eventName); + break; + +#if false // Not used in unit testing crawling + case WorkspaceChangeKind.SolutionRemoved: + EnqueueFullSolutionEvent(args.OldSolution, UnitTestingInvocationReasons.SolutionRemoved, eventName); + break; + + case WorkspaceChangeKind.SolutionCleared: + EnqueueFullSolutionEvent(args.OldSolution, UnitTestingInvocationReasons.SolutionRemoved, eventName); + break; +#else + case WorkspaceChangeKind.SolutionCleared: + case WorkspaceChangeKind.SolutionRemoved: + // Not used in unit testing crawling + break; +#endif + + case WorkspaceChangeKind.ProjectAdded: + Contract.ThrowIfNull(args.ProjectId); + EnqueueFullProjectEvent(args.NewSolution, args.ProjectId, UnitTestingInvocationReasons.DocumentAdded, eventName); + break; + + case WorkspaceChangeKind.ProjectChanged: + case WorkspaceChangeKind.ProjectReloaded: + Contract.ThrowIfNull(args.ProjectId); + EnqueueProjectChangedEvent(args.OldSolution, args.NewSolution, args.ProjectId, eventName); + break; + + case WorkspaceChangeKind.ProjectRemoved: + Contract.ThrowIfNull(args.ProjectId); + EnqueueFullProjectEvent(args.OldSolution, args.ProjectId, UnitTestingInvocationReasons.DocumentRemoved, eventName); + break; + + case WorkspaceChangeKind.DocumentAdded: + Contract.ThrowIfNull(args.DocumentId); + EnqueueFullDocumentEvent(args.NewSolution, args.DocumentId, UnitTestingInvocationReasons.DocumentAdded, eventName); + break; + + case WorkspaceChangeKind.DocumentReloaded: + case WorkspaceChangeKind.DocumentChanged: + Contract.ThrowIfNull(args.DocumentId); + EnqueueDocumentChangedEvent(args.OldSolution, args.NewSolution, args.DocumentId, eventName); + break; + + case WorkspaceChangeKind.DocumentRemoved: + Contract.ThrowIfNull(args.DocumentId); + EnqueueFullDocumentEvent(args.OldSolution, args.DocumentId, UnitTestingInvocationReasons.DocumentRemoved, eventName); + break; + + case WorkspaceChangeKind.AdditionalDocumentAdded: + case WorkspaceChangeKind.AdditionalDocumentRemoved: + case WorkspaceChangeKind.AdditionalDocumentChanged: + case WorkspaceChangeKind.AdditionalDocumentReloaded: + case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: + case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: + case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: + case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: + // If an additional file or .editorconfig has changed we need to reanalyze the entire project. + Contract.ThrowIfNull(args.ProjectId); + EnqueueFullProjectEvent(args.NewSolution, args.ProjectId, UnitTestingInvocationReasons.AdditionalDocumentChanged, eventName); + break; + + default: + throw ExceptionUtilities.UnexpectedValue(args.Kind); + } + } + +#if false // Not used in unit testing crawling + public void OnTextDocumentOpened(TextDocumentEventArgs e) + { + _eventProcessingQueue.ScheduleTask("OnTextDocumentOpened", + () => EnqueueDocumentWorkItemAsync(e.Document.Project, e.Document.Id, e.Document, UnitTestingInvocationReasons.DocumentOpened), _shutdownToken); + } + + public void OnTextDocumentClosed(TextDocumentEventArgs e) + { + _eventProcessingQueue.ScheduleTask("OnTextDocumentClosed", + () => EnqueueDocumentWorkItemAsync(e.Document.Project, e.Document.Id, e.Document, UnitTestingInvocationReasons.DocumentClosed), _shutdownToken); + } +#endif + + private void EnqueueSolutionChangedEvent(Solution oldSolution, Solution newSolution, string eventName) + { + _eventProcessingQueue.ScheduleTask( + eventName, + async () => + { + var solutionChanges = newSolution.GetChanges(oldSolution); + + // TODO: Async version for GetXXX methods? + foreach (var addedProject in solutionChanges.GetAddedProjects()) + { + await EnqueueFullProjectWorkItemAsync(addedProject, UnitTestingInvocationReasons.DocumentAdded).ConfigureAwait(false); + } + + foreach (var projectChanges in solutionChanges.GetProjectChanges()) + { + await EnqueueWorkItemAsync(projectChanges).ConfigureAwait(continueOnCapturedContext: false); + } + + foreach (var removedProject in solutionChanges.GetRemovedProjects()) + { + await EnqueueFullProjectWorkItemAsync(removedProject, UnitTestingInvocationReasons.DocumentRemoved).ConfigureAwait(false); + } + }, + _shutdownToken); + } + + private void EnqueueFullSolutionEvent(Solution solution, UnitTestingInvocationReasons invocationReasons, string eventName) + { + _eventProcessingQueue.ScheduleTask( + eventName, + async () => + { + foreach (var projectId in solution.ProjectIds) + { + await EnqueueFullProjectWorkItemAsync(solution.GetRequiredProject(projectId), invocationReasons).ConfigureAwait(false); + } + }, + _shutdownToken); + } + + private void EnqueueProjectChangedEvent(Solution oldSolution, Solution newSolution, ProjectId projectId, string eventName) + { + _eventProcessingQueue.ScheduleTask( + eventName, + async () => + { + var oldProject = oldSolution.GetRequiredProject(projectId); + var newProject = newSolution.GetRequiredProject(projectId); + + await EnqueueWorkItemAsync(newProject.GetChanges(oldProject)).ConfigureAwait(false); + }, + _shutdownToken); + } + + private void EnqueueFullProjectEvent(Solution solution, ProjectId projectId, UnitTestingInvocationReasons invocationReasons, string eventName) + { + _eventProcessingQueue.ScheduleTask(eventName, + () => EnqueueFullProjectWorkItemAsync(solution.GetRequiredProject(projectId), invocationReasons), _shutdownToken); + } + + private void EnqueueFullDocumentEvent(Solution solution, DocumentId documentId, UnitTestingInvocationReasons invocationReasons, string eventName) + { + _eventProcessingQueue.ScheduleTask( + eventName, + () => + { + var project = solution.GetRequiredProject(documentId.ProjectId); + return EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons); + }, + _shutdownToken); + } + + private void EnqueueDocumentChangedEvent(Solution oldSolution, Solution newSolution, DocumentId documentId, string eventName) + { + // document changed event is the special one. + _eventProcessingQueue.ScheduleTask( + eventName, + async () => + { + var oldProject = oldSolution.GetRequiredProject(documentId.ProjectId); + var newProject = newSolution.GetRequiredProject(documentId.ProjectId); + + await EnqueueChangedDocumentWorkItemAsync(oldProject.GetRequiredDocument(documentId), newProject.GetRequiredDocument(documentId)).ConfigureAwait(false); + + // If all features are enabled for source generated documents, the solution crawler needs to + // include them in incremental analysis. + if (_workspaceConfigurationService?.Options.EnableOpeningSourceGeneratedFiles == true) + { + // TODO: if this becomes a hot spot, we should be able to expose/access the dictionary + // underneath GetSourceGeneratedDocumentsAsync rather than create a new one here. + var oldProjectSourceGeneratedDocuments = await oldProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); + var oldProjectSourceGeneratedDocumentsById = oldProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); + var newProjectSourceGeneratedDocuments = await newProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); + var newProjectSourceGeneratedDocumentsById = newProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); + + foreach (var (oldDocumentId, _) in oldProjectSourceGeneratedDocumentsById) + { + if (!newProjectSourceGeneratedDocumentsById.ContainsKey(oldDocumentId)) + { + // This source generated document was removed + EnqueueFullDocumentEvent(oldSolution, oldDocumentId, UnitTestingInvocationReasons.DocumentRemoved, "OnWorkspaceChanged"); + } + } + + foreach (var (newDocumentId, newDocument) in newProjectSourceGeneratedDocumentsById) + { + if (!oldProjectSourceGeneratedDocumentsById.TryGetValue(newDocumentId, out var oldDocument)) + { + // This source generated document was added + EnqueueFullDocumentEvent(newSolution, newDocumentId, UnitTestingInvocationReasons.DocumentAdded, "OnWorkspaceChanged"); + } + else + { + // This source generated document may have changed + await EnqueueChangedDocumentWorkItemAsync(oldDocument, newDocument).ConfigureAwait(continueOnCapturedContext: false); + } + } + } + }, + _shutdownToken); + } + + private async Task EnqueueDocumentWorkItemAsync(Project project, DocumentId documentId, TextDocument? document, UnitTestingInvocationReasons invocationReasons, SyntaxNode? changedMember = null) + { + // we are shutting down + _shutdownToken.ThrowIfCancellationRequested(); + + var priorityService = project.GetLanguageService(); + document ??= project.GetTextDocument(documentId); + var sourceDocument = document as Document; + var isLowPriority = priorityService != null && sourceDocument != null && await priorityService.IsLowPriorityAsync(sourceDocument, _shutdownToken).ConfigureAwait(false); + + var currentMember = GetSyntaxPath(changedMember); + + // call to this method is serialized. and only this method does the writing. + _documentAndProjectWorkerProcessor.Enqueue( + new UnitTestingWorkItem(documentId, project.Language, invocationReasons, isLowPriority, currentMember, _listener.BeginAsyncOperation("WorkItem"))); + + // enqueue semantic work planner + if (invocationReasons.Contains(UnitTestingPredefinedInvocationReasons.SemanticChanged) && sourceDocument != null) + { + // must use "Document" here so that the snapshot doesn't go away. we need the snapshot to calculate p2p dependency graph later. + // due to this, we might hold onto solution (and things kept alive by it) little bit longer than usual. + _semanticChangeProcessor.Enqueue(project, documentId, sourceDocument, currentMember); + } + } + + private static Document GetRequiredDocument(Project project, DocumentId documentId, Document? document) + => document ?? project.GetRequiredDocument(documentId); + + private static SyntaxPath? GetSyntaxPath(SyntaxNode? changedMember) + { + // using syntax path might be too expansive since it will be created on every keystroke. + // but currently, we have no other way to track a node between two different tree (even for incrementally parsed one) + if (changedMember == null) + { + return null; + } + + return new SyntaxPath(changedMember); + } + + private async Task EnqueueFullProjectWorkItemAsync(Project project, UnitTestingInvocationReasons invocationReasons) + { + foreach (var documentId in project.DocumentIds) + await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); + + foreach (var documentId in project.AdditionalDocumentIds) + await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); + + foreach (var documentId in project.AnalyzerConfigDocumentIds) + await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); + + // If all features are enabled for source generated documents, the solution crawler needs to + // include them in incremental analysis. + if (_workspaceConfigurationService?.Options.EnableOpeningSourceGeneratedFiles == true) + { + foreach (var document in await project.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false)) + await EnqueueDocumentWorkItemAsync(project, document.Id, document, invocationReasons).ConfigureAwait(false); + } + } + + private async Task EnqueueWorkItemAsync(IUnitTestingIncrementalAnalyzer analyzer, UnitTestingReanalyzeScope scope +#if false // Not used in unit testing crawling + , bool highPriority +#endif + ) + { + var solution = _registration.GetSolutionToAnalyze(); + var invocationReasons = +#if false // Not used in unit testing crawling + highPriority ? UnitTestingInvocationReasons.ReanalyzeHighPriority : +#endif + UnitTestingInvocationReasons.Reanalyze; + + foreach (var (project, documentId) in scope.GetDocumentIds(solution)) + await EnqueueWorkItemAsync(analyzer, project, documentId, document: null, invocationReasons).ConfigureAwait(false); + } + + private async Task EnqueueWorkItemAsync( + IUnitTestingIncrementalAnalyzer analyzer, Project project, DocumentId documentId, Document? document, UnitTestingInvocationReasons invocationReasons) + { + var priorityService = project.GetLanguageService(); + var isLowPriority = priorityService != null && await priorityService.IsLowPriorityAsync( + GetRequiredDocument(project, documentId, document), _shutdownToken).ConfigureAwait(false); + + _documentAndProjectWorkerProcessor.Enqueue( + new UnitTestingWorkItem(documentId, project.Language, invocationReasons, + isLowPriority, analyzer, _listener.BeginAsyncOperation("WorkItem"))); + } + + private async Task EnqueueWorkItemAsync(ProjectChanges projectChanges) + { + await EnqueueProjectConfigurationChangeWorkItemAsync(projectChanges).ConfigureAwait(false); + + foreach (var addedDocumentId in projectChanges.GetAddedDocuments()) + await EnqueueDocumentWorkItemAsync(projectChanges.NewProject, addedDocumentId, document: null, UnitTestingInvocationReasons.DocumentAdded).ConfigureAwait(false); + + foreach (var changedDocumentId in projectChanges.GetChangedDocuments()) + { + await EnqueueChangedDocumentWorkItemAsync(projectChanges.OldProject.GetRequiredDocument(changedDocumentId), projectChanges.NewProject.GetRequiredDocument(changedDocumentId)) + .ConfigureAwait(continueOnCapturedContext: false); + } + + foreach (var removedDocumentId in projectChanges.GetRemovedDocuments()) + await EnqueueDocumentWorkItemAsync(projectChanges.OldProject, removedDocumentId, document: null, UnitTestingInvocationReasons.DocumentRemoved).ConfigureAwait(false); + } + + private async Task EnqueueProjectConfigurationChangeWorkItemAsync(ProjectChanges projectChanges) + { + var oldProject = projectChanges.OldProject; + var newProject = projectChanges.NewProject; + + // TODO: why solution changes return Project not ProjectId but ProjectChanges return DocumentId not Document? + var projectConfigurationChange = UnitTestingInvocationReasons.Empty; + +#if false // Not used in unit testing crawling + if (!object.Equals(oldProject.ParseOptions, newProject.ParseOptions)) + { + projectConfigurationChange = projectConfigurationChange.With(UnitTestingInvocationReasons.ProjectParseOptionChanged); + } +#endif + + if (projectChanges.GetAddedMetadataReferences().Any() || + projectChanges.GetAddedProjectReferences().Any() || + projectChanges.GetAddedAnalyzerReferences().Any() || + projectChanges.GetRemovedMetadataReferences().Any() || + projectChanges.GetRemovedProjectReferences().Any() || + projectChanges.GetRemovedAnalyzerReferences().Any() || + !object.Equals(oldProject.CompilationOptions, newProject.CompilationOptions) || + !object.Equals(oldProject.AssemblyName, newProject.AssemblyName) || + !object.Equals(oldProject.Name, newProject.Name) || + !object.Equals(oldProject.AnalyzerOptions, newProject.AnalyzerOptions) || + !object.Equals(oldProject.DefaultNamespace, newProject.DefaultNamespace) || + !object.Equals(oldProject.OutputFilePath, newProject.OutputFilePath) || + !object.Equals(oldProject.OutputRefFilePath, newProject.OutputRefFilePath) || + !oldProject.CompilationOutputInfo.Equals(newProject.CompilationOutputInfo) || + oldProject.State.RunAnalyzers != newProject.State.RunAnalyzers) + { + projectConfigurationChange = projectConfigurationChange.With(UnitTestingInvocationReasons.ProjectConfigurationChanged); + } + + if (!projectConfigurationChange.IsEmpty) + { + await EnqueueFullProjectWorkItemAsync(projectChanges.NewProject, projectConfigurationChange).ConfigureAwait(false); + } + } + + private async Task EnqueueChangedDocumentWorkItemAsync(Document oldDocument, Document newDocument) + { + var differenceService = newDocument.GetLanguageService(); + + if (differenceService == null) + { + // For languages that don't use a Roslyn syntax tree, they don't export a document difference service. + // The whole document should be considered as changed in that case. + await EnqueueDocumentWorkItemAsync(newDocument.Project, newDocument.Id, newDocument, UnitTestingInvocationReasons.DocumentChanged).ConfigureAwait(false); + } + else + { +#if false // Not used in unit testing crawling + var differenceResult = await differenceService.GetDifferenceAsync(oldDocument, newDocument, _shutdownToken).ConfigureAwait(false); +#else + var differenceResult = differenceService.GetDifference(oldDocument, newDocument, _shutdownToken); +#endif + + if (differenceResult != null) + await EnqueueDocumentWorkItemAsync(newDocument.Project, newDocument.Id, newDocument, differenceResult.ChangeType, differenceResult.ChangedMember).ConfigureAwait(false); + } + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly UnitTestingWorkCoordinator _workCoordinator; + + internal TestAccessor(UnitTestingWorkCoordinator workCoordinator) + { + _workCoordinator = workCoordinator; + } + + internal void WaitUntilCompletion(ImmutableArray workers) + { + var solution = _workCoordinator._registration.GetSolutionToAnalyze(); + var list = new List(); + + foreach (var project in solution.Projects) + { + foreach (var document in project.Documents) + { + list.Add(new UnitTestingWorkItem(document.Id, document.Project.Language, UnitTestingInvocationReasons.DocumentAdded, isLowPriority: false, activeMember: null, EmptyAsyncToken.Instance)); + } + } + + _workCoordinator._documentAndProjectWorkerProcessor.GetTestAccessor().WaitUntilCompletion(workers, list); + } + + internal void WaitUntilCompletion() + => _workCoordinator._documentAndProjectWorkerProcessor.GetTestAccessor().WaitUntilCompletion(); + } + } + + internal readonly struct UnitTestingReanalyzeScope + { + private readonly SolutionId? _solutionId; + private readonly ISet? _projectOrDocumentIds; + + public UnitTestingReanalyzeScope(SolutionId solutionId) + { + _solutionId = solutionId; + _projectOrDocumentIds = null; + } + + public UnitTestingReanalyzeScope(IEnumerable? projectIds = null, IEnumerable? documentIds = null) + { + projectIds ??= SpecializedCollections.EmptyEnumerable(); + documentIds ??= SpecializedCollections.EmptyEnumerable(); + + _solutionId = null; + _projectOrDocumentIds = new HashSet(projectIds); + + foreach (var documentId in documentIds) + { + if (_projectOrDocumentIds.Contains(documentId.ProjectId)) + { + continue; + } + + _projectOrDocumentIds.Add(documentId); + } + } + + public bool HasMultipleDocuments => _solutionId != null || _projectOrDocumentIds?.Count > 1; + + public string GetLanguagesStringForTelemetry(Solution solution) + { + if (_solutionId != null && solution.Id != _solutionId) + { + // return empty if given solution is not + // same as solution this scope is created for + return string.Empty; + } + + using var pool = SharedPools.Default>().GetPooledObject(); + if (_solutionId != null) + { + pool.Object.UnionWith(solution.State.ProjectStates.Select(kv => kv.Value.Language)); + return string.Join(",", pool.Object); + } + + Contract.ThrowIfNull(_projectOrDocumentIds); + + foreach (var projectOrDocumentId in _projectOrDocumentIds) + { + switch (projectOrDocumentId) + { + case ProjectId projectId: + var project = solution.GetProject(projectId); + if (project != null) + { + pool.Object.Add(project.Language); + } + + break; + case DocumentId documentId: + var document = solution.GetDocument(documentId); + if (document != null) + { + pool.Object.Add(document.Project.Language); + } + + break; + default: + throw ExceptionUtilities.UnexpectedValue(projectOrDocumentId); + } + } + + return string.Join(",", pool.Object); + } + + public int GetDocumentCount(Solution solution) + { + if (_solutionId != null && solution.Id != _solutionId) + { + return 0; + } + + var count = 0; + if (_solutionId != null) + { + foreach (var projectState in solution.State.ProjectStates) + { + count += projectState.Value.DocumentStates.Count; + } + + return count; + } + + Contract.ThrowIfNull(_projectOrDocumentIds); + + foreach (var projectOrDocumentId in _projectOrDocumentIds) + { + switch (projectOrDocumentId) + { + case ProjectId projectId: + var project = solution.GetProject(projectId); + if (project != null) + { + count += project.DocumentIds.Count; + } + + break; + case DocumentId documentId: + count++; + break; + default: + throw ExceptionUtilities.UnexpectedValue(projectOrDocumentId); + } + } + + return count; + } + + public IEnumerable<(Project project, DocumentId documentId)> GetDocumentIds(Solution solution) + { + if (_solutionId != null && solution.Id != _solutionId) + { + yield break; + } + + if (_solutionId != null) + { + foreach (var project in solution.Projects) + { + foreach (var documentId in project.DocumentIds) + yield return (project, documentId); + } + + yield break; + } + + Contract.ThrowIfNull(_projectOrDocumentIds); + + foreach (var projectOrDocumentId in _projectOrDocumentIds) + { + switch (projectOrDocumentId) + { + case ProjectId projectId: + { + var project = solution.GetProject(projectId); + if (project != null) + { + foreach (var documentId in project.DocumentIds) + yield return (project, documentId); + } + + break; + } + case DocumentId documentId: + { + var project = solution.GetProject(documentId.ProjectId); + if (project != null) + { + // ReanalyzeScopes are created and held in a queue before they are processed later; it's possible the document + // that we queued for is no longer present. + if (project.ContainsDocument(documentId)) + yield return (project, documentId); + } + + break; + } + } + } + } + } + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptTodoCommentDataServiceImplementation.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptTodoCommentDataServiceImplementation.cs index 870282e061486..ce11b8f3979b1 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptTodoCommentDataServiceImplementation.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptTodoCommentDataServiceImplementation.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.TodoComments; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCompletionServiceWithProviders.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCompletionServiceWithProviders.cs index 590f10c9c90b1..096e6bd962396 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCompletionServiceWithProviders.cs @@ -6,14 +6,17 @@ using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api { internal abstract class VSTypeScriptCompletionServiceWithProviders : CompletionService { + // Pass in NullProvider since it's only used for testing project reference based CompletionProvider, + // which TypeScript does not need. internal VSTypeScriptCompletionServiceWithProviders(Workspace workspace) - : base(workspace.Services.SolutionServices) + : base(workspace.Services.SolutionServices, AsynchronousOperationListenerProvider.NullProvider) { } diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigateToSearchService.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigateToSearchService.cs index f258f7dc0b8b1..af17c03ffbf73 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigateToSearchService.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptNavigateToSearchService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -35,62 +36,58 @@ public VSTypeScriptNavigateToSearchService( public bool CanFilter => _searchService?.CanFilter ?? false; - public async Task SearchDocumentAsync( + public async IAsyncEnumerable SearchDocumentAsync( Document document, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { if (_searchService != null) { var results = await _searchService.SearchDocumentAsync(document, searchPattern, kinds, cancellationToken).ConfigureAwait(false); foreach (var result in results) - await onResultFound(Convert(result)).ConfigureAwait(false); + yield return Convert(result); } } - public async Task SearchProjectAsync( + public async IAsyncEnumerable SearchProjectAsync( Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { if (_searchService != null) { var results = await _searchService.SearchProjectAsync(project, priorityDocuments, searchPattern, kinds, cancellationToken).ConfigureAwait(false); foreach (var result in results) - await onResultFound(Convert(result)).ConfigureAwait(false); + yield return Convert(result); } } - public Task SearchCachedDocumentsAsync( + public IAsyncEnumerable SearchCachedDocumentsAsync( Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, CancellationToken cancellationToken) { // we don't support searching cached documents. - return Task.CompletedTask; + return AsyncEnumerable.Empty; } - public Task SearchGeneratedDocumentsAsync( + public IAsyncEnumerable SearchGeneratedDocumentsAsync( Project project, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, CancellationToken cancellationToken) { // we don't support searching generated documents. - return Task.CompletedTask; + return AsyncEnumerable.Empty; } private static INavigateToSearchResult Convert(IVSTypeScriptNavigateToSearchResult result) 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/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableSymbol.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableSymbol.cs index d6480217c9ce4..cbf2d3f990661 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableSymbol.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableSymbol.cs @@ -91,9 +91,9 @@ public override bool GetUseSaferDeclarationBehavior(CancellationToken cancellati } public override SyntaxToken GetOriginalIdentifierToken(CancellationToken cancellationToken) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); - public override SyntaxAnnotation IdentifierTokenAnnotation => throw ExceptionUtilities.Unreachable; + public override SyntaxAnnotation IdentifierTokenAnnotation => throw ExceptionUtilities.Unreachable(); public override void AddIdentifierTokenAnnotationPair( List> annotations, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzer.cs b/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzer.cs deleted file mode 100644 index 389361e760886..0000000000000 --- a/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzer.cs +++ /dev/null @@ -1,71 +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.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.SolutionCrawler; - -namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree -{ - internal partial class SymbolTreeInfoIncrementalAnalyzerProvider - { - private class SymbolTreeInfoIncrementalAnalyzer : IncrementalAnalyzerBase - { - private readonly Workspace _workspace; - - public SymbolTreeInfoIncrementalAnalyzer(Workspace workspace) - => _workspace = workspace; - - public override async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - { - var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); - var isMethodBodyEdit = bodyOpt != null; - - if (client != null) - { - await client.TryInvokeAsync( - document.Project, (service, checksum, cancellationToken) => - service.AnalyzeDocumentAsync(checksum, document.Id, isMethodBodyEdit, cancellationToken), - cancellationToken).ConfigureAwait(false); - return; - } - - var service = _workspace.Services.GetRequiredService(); - await service.AnalyzeDocumentAsync(document, isMethodBodyEdit, cancellationToken).ConfigureAwait(false); - } - - public override async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) - { - var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); - if (client != null) - { - await client.TryInvokeAsync( - project, (service, checksum, cancellationToken) => - service.AnalyzeProjectAsync(checksum, project.Id, cancellationToken), - cancellationToken).ConfigureAwait(false); - return; - } - - var service = _workspace.Services.GetRequiredService(); - await service.AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); - } - - public override async Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) - { - var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); - if (client != null) - { - await client.TryInvokeAsync( - (service, cancellationToken) => service.RemoveProjectAsync(projectId, cancellationToken), - cancellationToken).ConfigureAwait(false); - return; - } - - var service = _workspace.Services.GetRequiredService(); - service.RemoveProject(projectId); - } - } - } -} diff --git a/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzerProvider.cs deleted file mode 100644 index 443c68b0eb854..0000000000000 --- a/src/Features/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ /dev/null @@ -1,40 +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 Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.SolutionCrawler; - -namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree -{ - /// - /// Features like add-using want to be able to quickly search symbol indices for projects and - /// metadata. However, creating those indices can be expensive. As such, we don't want to - /// construct them during the add-using process itself. Instead, we expose this type as an - /// Incremental-Analyzer to walk our projects/metadata in the background to keep the indices - /// up to date. - /// - /// We also then export this type as a service that can give back the index for a project or - /// metadata dll on request. If the index has been produced then it will be returned and - /// can be used by add-using. Otherwise, nothing is returned and no results will be found. - /// - /// This means that as the project is being indexed, partial results may be returned. However - /// once it is fully indexed, then total results will be returned. - /// - [ExportIncrementalAnalyzerProvider( - highPriorityForActiveFile: false, name: nameof(SymbolTreeInfoIncrementalAnalyzerProvider), - workspaceKinds: new[] { WorkspaceKind.Host }), Shared] - internal partial class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SymbolTreeInfoIncrementalAnalyzerProvider() - { - } - - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - => new SymbolTreeInfoIncrementalAnalyzer(workspace); - } -} diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.DetachedDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.DetachedDefinitionItem.cs index 2fe4cd287abac..51c6444176b1f 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.DetachedDefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.DetachedDefinitionItem.cs @@ -2,17 +2,20 @@ // 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.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Collections; +using Roslyn.Utilities; using static Microsoft.CodeAnalysis.FindUsages.DefinitionItem; namespace Microsoft.CodeAnalysis.FindUsages { [DataContract] - internal readonly struct DetachedDefinitionItem + internal readonly struct DetachedDefinitionItem : IEquatable { [DataMember(Order = 0)] public readonly ImmutableArray Tags; @@ -51,6 +54,21 @@ public DetachedDefinitionItem( SourceSpans = sourceSpans; } + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => obj is DetachedDefinitionItem item && Equals(item); + + public bool Equals(DetachedDefinitionItem other) + => this.DisplayIfNoReferences == other.DisplayIfNoReferences && + this.Tags.SequenceEqual(other.Tags) && + this.DisplayParts.SequenceEqual(other.DisplayParts) && + this.OriginationParts.SequenceEqual(other.OriginationParts) && + this.SourceSpans.SequenceEqual(other.SourceSpans) && + this.Properties.SetEquals(other.Properties) && + this.DisplayableProperties.SetEquals(other.DisplayableProperties); + public async Task TryRehydrateAsync(Solution solution, CancellationToken cancellationToken) { using var converted = TemporaryArray.Empty; diff --git a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs index 6f22acc10b59e..078cf29a689a9 100644 --- a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs +++ b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs @@ -130,7 +130,7 @@ public async ValueTask OnDefinitionFoundAsync(SerializableDefinitionItem definit } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -143,7 +143,7 @@ public async ValueTask OnReferenceFoundAsync(SerializableSourceReferenceItem ref } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs b/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs index a21a246a0b0be..786896b0f3072 100644 --- a/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs @@ -222,7 +222,7 @@ private static SyntaxNode GenerateStatement( CodeGenerationOperatorKind.LessThanOrEqual => generator.LessThanOrEqualExpression(compareToCall, zero), CodeGenerationOperatorKind.GreaterThan => generator.GreaterThanExpression(compareToCall, zero), CodeGenerationOperatorKind.GreaterThanOrEqual => generator.GreaterThanOrEqualExpression(compareToCall, zero), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; return generator.ReturnStatement(comparison); @@ -262,7 +262,7 @@ private static string GetOperatorName(CodeGenerationOperatorKind kind) CodeGenerationOperatorKind.LessThanOrEqual => WellKnownMemberNames.LessThanOrEqualOperatorName, CodeGenerationOperatorKind.GreaterThan => WellKnownMemberNames.GreaterThanOperatorName, CodeGenerationOperatorKind.GreaterThanOrEqual => WellKnownMemberNames.GreaterThanOrEqualOperatorName, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; } } diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 9bbd920cd6b52..bfd35f88c02f3 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -419,7 +419,7 @@ private static int GetStatementIndex(ChildSyntaxList children, SyntaxNode statem index++; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private void DetermineFieldType( 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/InheritanceMargin/InheritanceMarginItem.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs index 989813a71c8bb..cad0b28747b86 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs @@ -2,16 +2,16 @@ // 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.Linq; using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InheritanceMargin { [DataContract] - internal readonly struct InheritanceMarginItem + internal readonly struct InheritanceMarginItem : IEquatable { /// /// Line number used to show the margin for the member. @@ -58,6 +58,19 @@ public InheritanceMarginItem( TargetItems = targetItems; } + public override int GetHashCode() + => throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => obj is InheritanceMarginItem item && Equals(item); + + public bool Equals(InheritanceMarginItem other) + => this.LineNumber == other.LineNumber && + this.TopLevelDisplayText == other.TopLevelDisplayText && + this.Glyph == other.Glyph && + this.DisplayTexts.SequenceEqual(other.DisplayTexts) && + this.TargetItems.SequenceEqual(other.TargetItems); + public static InheritanceMarginItem? CreateOrdered( int lineNumber, string? topLevelDisplayText, diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceTargetItem.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceTargetItem.cs index 55e7032f71400..a662dd5c2350a 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceTargetItem.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceTargetItem.cs @@ -2,10 +2,10 @@ // 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.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindUsages; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InheritanceMargin { @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.InheritanceMargin /// Information used to decided the margin image and responsible for performing navigations /// [DataContract] - internal readonly struct InheritanceTargetItem + internal readonly record struct InheritanceTargetItem { /// /// Indicate the inheritance relationship between the target and member. diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 34b3d469c4d01..9d65d998e6179 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -585,7 +585,7 @@ private SyntaxNode CreateArgumentException( { nameof(string.IsNullOrEmpty) => new LocalizableResourceString(nameof(FeaturesResources._0_cannot_be_null_or_empty), FeaturesResources.ResourceManager, typeof(FeaturesResources)).ToString(), nameof(string.IsNullOrWhiteSpace) => new LocalizableResourceString(nameof(FeaturesResources._0_cannot_be_null_or_whitespace), FeaturesResources.ResourceManager, typeof(FeaturesResources)).ToString(), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; // The resource string is written to be shown in a UI and is not necessarily valid code, but we're diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs b/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs index 3995f8abe8900..f010e6e25d2a2 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs @@ -301,7 +301,7 @@ private IFieldSymbol CreateField( // We place a special rule in s_builtInRules that matches all fields. So we should // always find a matching rule. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static string GenerateUniqueName(IParameterSymbol parameter, ImmutableArray parameterNameParts, NamingRule rule) @@ -361,7 +361,7 @@ private IPropertySymbol CreateProperty( // We place a special rule in s_builtInRules that matches all properties. So we should // always find a matching rule. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private async Task AddAllSymbolInitializationsAsync( @@ -480,7 +480,7 @@ private async Task AddSingleSymbolInitializationAsync( } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }); } diff --git a/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs index c1a316d806e61..b8fdf9170393f 100644 --- a/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs @@ -325,7 +325,7 @@ private int GetNearmostParentJumpStatementRawKind(SyntaxNode ifNode) } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private bool IsEmptyStatementRange(StatementRange statementRange) diff --git a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs index 18ae78ea6f8ae..965a71e8e333c 100644 --- a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs +++ b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs @@ -34,7 +34,7 @@ private ImmutableArray GetDelegateAnonymousTypeParts( { using var _ = ArrayBuilder.GetInstance(out var parts); - var invokeMethod = anonymousType.DelegateInvokeMethod ?? throw ExceptionUtilities.Unreachable; + var invokeMethod = anonymousType.DelegateInvokeMethod ?? throw ExceptionUtilities.Unreachable(); parts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.Keyword, symbol: null, SyntaxFactsService.GetText(SyntaxFactsService.SyntaxKinds.DelegateKeyword))); diff --git a/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsAggregationService.cs b/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsAggregationService.cs new file mode 100644 index 0000000000000..be5ed48cebc73 --- /dev/null +++ b/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsAggregationService.cs @@ -0,0 +1,77 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using static Microsoft.CodeAnalysis.EditAndContinue.TraceLog; + +namespace Microsoft.CodeAnalysis.LegacySolutionEvents +{ + /// + /// This is a legacy api intended only for existing SolutionCrawler partners to continue to function (albeit with + /// ownership of that crawling task now belonging to the partner team, not roslyn). It should not be used for any + /// new services. + /// + internal interface ILegacySolutionEventsAggregationService : IWorkspaceService + { + bool ShouldReportChanges(SolutionServices services); + + ValueTask OnWorkspaceChangedAsync(WorkspaceChangeEventArgs args, CancellationToken cancellationToken); +#if false // Not used in unit testing crawling + ValueTask OnTextDocumentOpenedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken); + ValueTask OnTextDocumentClosedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken); +#endif + } + + [ExportWorkspaceService(typeof(ILegacySolutionEventsAggregationService)), Shared] + internal class DefaultLegacySolutionEventsAggregationService : ILegacySolutionEventsAggregationService + { + private readonly ImmutableArray> _eventsServices; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DefaultLegacySolutionEventsAggregationService( + [ImportMany] IEnumerable> eventsServices) + { + _eventsServices = eventsServices.ToImmutableArray(); + } + + public bool ShouldReportChanges(SolutionServices services) + { + foreach (var service in _eventsServices) + { + if (service.Value.ShouldReportChanges(services)) + return true; + } + + return false; + } + + public async ValueTask OnWorkspaceChangedAsync(WorkspaceChangeEventArgs args, CancellationToken cancellationToken) + { + foreach (var service in _eventsServices) + await service.Value.OnWorkspaceChangedAsync(args, cancellationToken).ConfigureAwait(false); + } + +#if false // Not used in unit testing crawling + public async ValueTask OnTextDocumentOpenedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken) + { + foreach (var service in _eventsServices) + await service.Value.OnTextDocumentOpenedAsync(args, cancellationToken).ConfigureAwait(false); + } + + public async ValueTask OnTextDocumentClosedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken) + { + foreach (var service in _eventsServices) + await service.Value.OnTextDocumentClosedAsync(args, cancellationToken).ConfigureAwait(false); + } +#endif + } +} diff --git a/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsListener.cs b/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsListener.cs new file mode 100644 index 0000000000000..8b11939aa14f6 --- /dev/null +++ b/src/Features/Core/Portable/LegacySolutionEvents/ILegacySolutionEventsListener.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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.LegacySolutionEvents +{ + /// + /// This is a legacy api intended only for existing SolutionCrawler partners to continue to function (albeit with + /// ownership of that crawling task now belonging to the partner team, not roslyn). It should not be used for any + /// new services. + /// + internal interface ILegacySolutionEventsListener + { + bool ShouldReportChanges(SolutionServices services); + ValueTask OnWorkspaceChangedAsync(WorkspaceChangeEventArgs args, CancellationToken cancellationToken); + +#if false // Not used in unit testing crawling + ValueTask OnTextDocumentOpenedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken); + ValueTask OnTextDocumentClosedAsync(TextDocumentEventArgs args, CancellationToken cancellationToken); +#endif + } +} diff --git a/src/Features/Core/Portable/LegacySolutionEvents/IRemoteLegacySolutionEventsAggregationService.cs b/src/Features/Core/Portable/LegacySolutionEvents/IRemoteLegacySolutionEventsAggregationService.cs new file mode 100644 index 0000000000000..2f514530c9ba5 --- /dev/null +++ b/src/Features/Core/Portable/LegacySolutionEvents/IRemoteLegacySolutionEventsAggregationService.cs @@ -0,0 +1,27 @@ +// 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.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.LegacySolutionEvents +{ + /// + /// This is a legacy api intended only for existing SolutionCrawler partners to continue to function (albeit with + /// ownership of that crawling task now belonging to the partner team, not roslyn). It should not be used for any + /// new services. + /// + internal interface IRemoteLegacySolutionEventsAggregationService + { + ValueTask ShouldReportChangesAsync(CancellationToken cancellationToken); + + /// + /// + /// + /// + /// + ValueTask OnWorkspaceChangedAsync(Checksum oldSolutionChecksum, Checksum newSolutionChecksum, WorkspaceChangeKind kind, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 62edf7c350005..fffcb379a7045 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -48,7 +48,11 @@ public async Task AddSourceToAsync( generateDocumentationComments: true, mergeAttributes: false, autoInsertionLocation: false), - new CodeAndImportGenerationOptions(options.GenerationOptions, options.CleanupOptions.AddImportOptions).CreateProvider()); + new CodeAndImportGenerationOptions() + { + GenerationOptions = options.GenerationOptions, + AddImportOptions = options.CleanupOptions.AddImportOptions + }.CreateProvider()); // Add the interface of the symbol to the top of the root namespace document = await CodeGenerator.AddNamespaceOrTypeDeclarationAsync( diff --git a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs index 9a04b4b293bda..b876d3e28ac9a 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DecompilationMetadataAsSourceFileProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Composition; using System.Globalization; @@ -13,9 +14,11 @@ 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; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -27,10 +30,25 @@ internal class DecompilationMetadataAsSourceFileProvider : IMetadataAsSourceFile { internal const string ProviderName = "Decompilation"; + /// + /// Accessed only in and , both of which + /// are called under a lock in . So this is safe as a plain + /// dictionary. + /// private readonly Dictionary _keyToInformation = new(); - private readonly Dictionary _generatedFilenameToInformation = new(StringComparer.OrdinalIgnoreCase); + /// + /// Accessed both in and in UI thread operations. Those should not + /// generally run concurrently. However, to be safe, we make this a concurrent dictionary to be safe to that + /// potentially happening. + /// + private readonly ConcurrentDictionary _generatedFilenameToInformation = new(StringComparer.OrdinalIgnoreCase); + private readonly IImplementationAssemblyLookupService _implementationAssemblyLookupService; + + /// + /// Only accessed and mutated from UI thread. + /// private IBidirectionalMap _openedDocumentIds = BidirectionalMap.Empty; [ImportingConstructor] @@ -228,8 +246,17 @@ private async Task RelocateSymbol_NoLockAsync(Workspace workspace, Met return await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false); } - public bool ShouldCollapseOnOpen(string filePath, BlockStructureOptions blockStructureOptions) + private static void AssertIsMainThread(MetadataAsSourceWorkspace workspace) + { + Contract.ThrowIfNull(workspace); + var threadingService = workspace.Services.GetRequiredService().Service; + Contract.ThrowIfFalse(threadingService.IsOnMainThread); + } + + public bool ShouldCollapseOnOpen(MetadataAsSourceWorkspace workspace, string filePath, BlockStructureOptions blockStructureOptions) { + AssertIsMainThread(workspace); + if (_generatedFilenameToInformation.TryGetValue(filePath, out var info)) { return info.SignaturesOnly @@ -240,8 +267,10 @@ public bool ShouldCollapseOnOpen(string filePath, BlockStructureOptions blockStr return false; } - public bool TryAddDocumentToWorkspace(Workspace workspace, string filePath, SourceTextContainer sourceTextContainer) + public bool TryAddDocumentToWorkspace(MetadataAsSourceWorkspace workspace, string filePath, SourceTextContainer sourceTextContainer) { + AssertIsMainThread(workspace); + if (_generatedFilenameToInformation.TryGetValue(filePath, out var fileInfo)) { Contract.ThrowIfTrue(_openedDocumentIds.ContainsKey(fileInfo)); @@ -260,25 +289,27 @@ public bool TryAddDocumentToWorkspace(Workspace workspace, string filePath, Sour return false; } - public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) + public bool TryRemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, string filePath) { + AssertIsMainThread(workspace); + if (_generatedFilenameToInformation.TryGetValue(filePath, out var fileInfo)) { if (_openedDocumentIds.ContainsKey(fileInfo)) - { return RemoveDocumentFromWorkspace(workspace, fileInfo); - } } return false; } - private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGeneratedFileInfo fileInfo) + private bool RemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, MetadataAsSourceGeneratedFileInfo fileInfo) { + AssertIsMainThread(workspace); + 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); @@ -301,19 +332,13 @@ private bool RemoveDocumentFromWorkspace(Workspace workspace, MetadataAsSourceGe return project; } - public void CleanupGeneratedFiles(Workspace? workspace) + public void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace) { // Clone the list so we don't break our own enumeration - var generatedFileInfoList = _generatedFilenameToInformation.Values.ToList(); - - foreach (var generatedFileInfo in generatedFileInfoList) + foreach (var generatedFileInfo in _generatedFilenameToInformation.Values.ToList()) { if (_openedDocumentIds.ContainsKey(generatedFileInfo)) - { - Contract.ThrowIfNull(workspace); - RemoveDocumentFromWorkspace(workspace, generatedFileInfo); - } } _generatedFilenameToInformation.Clear(); diff --git a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs index aa43bb37c8d89..b9954f876fab7 100644 --- a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs @@ -19,30 +19,31 @@ internal interface IMetadataAsSourceFileProvider MetadataAsSourceWorkspace metadataWorkspace, Workspace sourceWorkspace, Project sourceProject, ISymbol symbol, bool signaturesOnly, MetadataAsSourceOptions options, string tempPath, CancellationToken cancellationToken); /// - /// Called when the file returned from needs to be added to the workspace, - /// to be opened. Will be called under a lock to prevent concurrent access. + /// Called to clean up any state. Will be called under a lock to prevent concurrent access. /// - bool TryAddDocumentToWorkspace(Workspace workspace, string filePath, SourceTextContainer sourceTextContainer); + void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace); /// - /// Called when the file is being closed, and so needs to be removed from the workspace. - /// Will be called under a lock to prevent concurrent access. + /// Called when the file returned from needs to be added to the workspace, + /// to be opened. Will be called on the main thread of the workspace host. /// - bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath); + bool TryAddDocumentToWorkspace(MetadataAsSourceWorkspace workspace, string filePath, SourceTextContainer sourceTextContainer); /// - /// Called to clean up any state. Will be called under a lock to prevent concurrent access. + /// Called when the file is being closed, and so needs to be removed from the workspace. Will be called on the + /// main thread of the workspace host. /// - void CleanupGeneratedFiles(Workspace? workspace); + bool TryRemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, string filePath); /// - /// Maps from a document to its project for the purposes of symbol mapping via + /// Called to determine if the file should be collapsed by default when opened for the first time. Will be + /// called on the main thread of the workspace host. /// - Project? MapDocument(Document document); + bool ShouldCollapseOnOpen(MetadataAsSourceWorkspace workspace, string filePath, BlockStructureOptions blockStructureOptions); /// - /// Called to determine if the file should be collapsed by default when opened for the first time + /// Maps from a document to its project for the purposes of symbol mapping via /// - bool ShouldCollapseOnOpen(string filePath, BlockStructureOptions blockStructureOptions); + Project? MapDocument(Document document); } } diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFileService.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFileService.cs index 747cbc3514926..40eabf98cee47 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFileService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFileService.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -24,14 +25,22 @@ namespace Microsoft.CodeAnalysis.MetadataAsSource internal class MetadataAsSourceFileService : IMetadataAsSourceFileService { /// - /// A lock to guard parallel accesses to this type. In practice, we presume that it's not - /// an important scenario that we can be generating multiple documents in parallel, and so - /// we simply take this lock around all public entrypoints to enforce sequential access. + /// Set of providers that can be used to generate source for a symbol (for example, by decompiling, or by + /// extracting it from a pdb). /// - private readonly SemaphoreSlim _gate = new(initialCount: 1); + private readonly ImmutableArray> _providers; + /// + /// Workspace created the first time we generate any metadata for any symbol. + /// private MetadataAsSourceWorkspace? _workspace; + /// + /// A lock to guard the mutex and filesystem data below. We want to ensure we generate into that and clean that + /// up safely. + /// + private readonly SemaphoreSlim _gate = new(initialCount: 1); + /// /// We create a mutex so other processes can see if our directory is still alive. We destroy the mutex when /// we purge our generated files. @@ -39,7 +48,6 @@ internal class MetadataAsSourceFileService : IMetadataAsSourceFileService private Mutex? _mutex; private string? _rootTemporaryPathWithGuid; private readonly string _rootTemporaryPath; - private readonly ImmutableArray> _providers; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -103,21 +111,31 @@ public async Task GetGeneratedFileAsync( } // The decompilation provider can always return something - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); + } + + private static void AssertIsMainThread(MetadataAsSourceWorkspace workspace) + { + var threadingService = workspace.Services.GetRequiredService().Service; + Contract.ThrowIfFalse(threadingService.IsOnMainThread); } public bool TryAddDocumentToWorkspace(string filePath, SourceTextContainer sourceTextContainer) { - using (_gate.DisposableWait()) + // If we haven't even created a MetadataAsSource workspace yet, then this file definitely cannot be added to + // it. This happens when the MiscWorkspace calls in to just see if it can attach this document to the + // MetadataAsSource instead of itself. + var workspace = _workspace; + if (workspace != null) { + AssertIsMainThread(workspace); + foreach (var provider in _providers) { if (!provider.IsValueCreated) continue; - Contract.ThrowIfNull(_workspace); - - if (provider.Value.TryAddDocumentToWorkspace(_workspace, filePath, sourceTextContainer)) + if (provider.Value.TryAddDocumentToWorkspace(workspace, filePath, sourceTextContainer)) return true; } } @@ -127,16 +145,20 @@ public bool TryAddDocumentToWorkspace(string filePath, SourceTextContainer sourc public bool TryRemoveDocumentFromWorkspace(string filePath) { - using (_gate.DisposableWait()) + // If we haven't even created a MetadataAsSource workspace yet, then this file definitely cannot be removed + // from it. This happens when the MiscWorkspace is hearing about a doc closing, and calls into the + // MetadataAsSource system to see if it owns the file and should handle that event. + var workspace = _workspace; + if (workspace != null) { + AssertIsMainThread(workspace); + foreach (var provider in _providers) { if (!provider.IsValueCreated) continue; - Contract.ThrowIfNull(_workspace); - - if (provider.Value.TryRemoveDocumentFromWorkspace(_workspace, filePath)) + if (provider.Value.TryRemoveDocumentFromWorkspace(workspace, filePath)) return true; } } @@ -149,18 +171,31 @@ public bool ShouldCollapseOnOpen(string? filePath, BlockStructureOptions blockSt if (filePath is null) return false; - using (_gate.DisposableWait()) + var workspace = _workspace; + + if (workspace == null) { - foreach (var provider in _providers) + try { - if (!provider.IsValueCreated) - continue; + throw new InvalidOperationException( + $"'{nameof(ShouldCollapseOnOpen)}' should only be called once outlining has already confirmed that '{filePath}' is from the {nameof(MetadataAsSourceWorkspace)}"); + } + catch (Exception ex) when (FatalError.ReportAndCatch(ex)) + { + } - Contract.ThrowIfNull(_workspace); + return false; + } - if (provider.Value.ShouldCollapseOnOpen(filePath, blockStructureOptions)) - return true; - } + AssertIsMainThread(workspace); + + foreach (var provider in _providers) + { + if (!provider.IsValueCreated) + continue; + + if (provider.Value.ShouldCollapseOnOpen(workspace, filePath, blockStructureOptions)) + return true; } return false; @@ -209,14 +244,18 @@ public void CleanupGeneratedFiles() _rootTemporaryPathWithGuid = null; } - // Only cleanup for providers that have actually generated a file. This keeps us from - // accidentally loading lazy providers on cleanup that weren't used - foreach (var provider in _providers) + // Only cleanup for providers that have actually generated a file. This keeps us from accidentally loading + // lazy providers on cleanup that weren't used + var workspace = _workspace; + if (workspace != null) { - if (!provider.IsValueCreated) - continue; + foreach (var provider in _providers) + { + if (!provider.IsValueCreated) + continue; - provider.Value.CleanupGeneratedFiles(_workspace); + provider.Value.CleanupGeneratedFiles(workspace); + } } try @@ -228,7 +267,6 @@ public void CleanupGeneratedFiles() // Let's look through directories to delete. foreach (var directoryInfo in new DirectoryInfo(_rootTemporaryPath).EnumerateDirectories()) { - // Is there a mutex for this one? if (Mutex.TryOpenExisting(CreateMutexName(directoryInfo.Name), out var acquiredMutex)) { diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceGeneratedFileInfo.cs index 5729d27c551f0..eddc0c911096c 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 { @@ -46,6 +47,7 @@ public MetadataAsSourceGeneratedFileInfo(string rootPath, Workspace sourceWorksp } 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 +72,34 @@ 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 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); var generatedDocumentId = DocumentId.CreateNewId(projectId); var generatedDocument = DocumentInfo.Create( generatedDocumentId, Path.GetFileName(TemporaryFilePath), + loader: loadFileFromDisk ? new WorkspaceFileTextLoader(workspace.Services.SolutionServices, TemporaryFilePath, Encoding) : null, filePath: TemporaryFilePath, - loader: loadFileFromDisk ? new FileTextLoader(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/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs index a1522509ae84a..e5d8c6c65fa66 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs @@ -2,10 +2,11 @@ // 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.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; @@ -13,6 +14,7 @@ using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; @@ -52,17 +54,15 @@ private static bool ShouldSearchCachedDocuments( return cachedIndexMap != null && stringTable != null; } - public async Task SearchCachedDocumentsAsync( + public async IAsyncEnumerable SearchCachedDocumentsAsync( Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var solution = project.Solution; - var onItemFound = GetOnItemFoundCallback(solution, activeDocument, onResultFound, cancellationToken); var documentKeys = project.Documents.SelectAsArray(DocumentKey.ToDocumentKey); var priorityDocumentKeys = priorityDocuments.SelectAsArray(DocumentKey.ToDocumentKey); @@ -70,32 +70,36 @@ public async Task SearchCachedDocumentsAsync( var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { - var callback = new NavigateToSearchServiceCallback(onItemFound); - await client.TryInvokeAsync( - (service, callbackId, cancellationToken) => - service.SearchCachedDocumentsAsync(documentKeys, priorityDocumentKeys, searchPattern, kinds.ToImmutableArray(), callbackId, cancellationToken), - callback, cancellationToken).ConfigureAwait(false); + var result = client.TryInvokeStreamAsync( + (service, cancellationToken) => + service.SearchCachedDocumentsAsync(documentKeys, priorityDocumentKeys, searchPattern, kinds.ToImmutableArray(), cancellationToken), + cancellationToken); - return; + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; } + else + { + var storageService = solution.Services.GetPersistentStorageService(); + var result = SearchCachedDocumentsInCurrentProcessAsync( + storageService, documentKeys, priorityDocumentKeys, searchPattern, kinds, cancellationToken); - var storageService = solution.Services.GetPersistentStorageService(); - await SearchCachedDocumentsInCurrentProcessAsync( - storageService, documentKeys, priorityDocumentKeys, searchPattern, kinds, onItemFound, cancellationToken).ConfigureAwait(false); + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; + } } - public static async Task SearchCachedDocumentsInCurrentProcessAsync( + public static async IAsyncEnumerable SearchCachedDocumentsInCurrentProcessAsync( IChecksummedPersistentStorageService storageService, ImmutableArray documentKeys, ImmutableArray priorityDocumentKeys, string searchPattern, IImmutableSet kinds, - Func onItemFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { // Quick abort if OOP is now fully loaded. if (!ShouldSearchCachedDocuments(out _, out _)) - return; + yield break; var highPriDocsSet = priorityDocumentKeys.ToSet(); var lowPriDocs = documentKeys.WhereAsArray(d => !highPriDocsSet.Contains(d)); @@ -104,40 +108,35 @@ public static async Task SearchCachedDocumentsInCurrentProcessAsync( var (patternName, patternContainer) = PatternMatcher.GetNameAndContainer(searchPattern); var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); - await SearchCachedDocumentsInCurrentProcessAsync( + await foreach (var item in SearchCachedDocumentsInCurrentProcessAsync( storageService, patternName, patternContainer, declaredSymbolInfoKindsSet, - onItemFound, priorityDocumentKeys, cancellationToken).ConfigureAwait(false); + priorityDocumentKeys, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } - await SearchCachedDocumentsInCurrentProcessAsync( + await foreach (var item in SearchCachedDocumentsInCurrentProcessAsync( storageService, patternName, patternContainer, declaredSymbolInfoKindsSet, - onItemFound, lowPriDocs, cancellationToken).ConfigureAwait(false); + lowPriDocs, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } } - private static async Task SearchCachedDocumentsInCurrentProcessAsync( + private static IAsyncEnumerable SearchCachedDocumentsInCurrentProcessAsync( IChecksummedPersistentStorageService storageService, string patternName, string? patternContainer, DeclaredSymbolInfoKindSet kinds, - Func onItemFound, ImmutableArray documentKeys, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var tasks); + using var _ = ArrayBuilder>.GetInstance(out var builder); foreach (var documentKey in documentKeys) - { - tasks.Add(Task.Run(async () => - { - var index = await GetIndexAsync(storageService, documentKey, cancellationToken).ConfigureAwait(false); - if (index == null) - return; - - await ProcessIndexAsync( - documentKey.Id, document: null, patternName, patternContainer, kinds, onItemFound, index, cancellationToken).ConfigureAwait(false); - }, cancellationToken)); - } + builder.Add(ProcessStaleIndexAsync(storageService, patternName, patternContainer, kinds, documentKey, cancellationToken)); - await Task.WhenAll(tasks).ConfigureAwait(false); + return builder.ToImmutable().MergeAsync(cancellationToken); } private static Task GetIndexAsync( diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs index 18d75e7903bb3..5405decd01eaa 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PatternMatching; @@ -14,41 +16,42 @@ namespace Microsoft.CodeAnalysis.NavigateTo { internal abstract partial class AbstractNavigateToSearchService { - public async Task SearchGeneratedDocumentsAsync( + public async IAsyncEnumerable SearchGeneratedDocumentsAsync( Project project, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var solution = project.Solution; - var onItemFound = GetOnItemFoundCallback(solution, activeDocument, onResultFound, cancellationToken); var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { - var callback = new NavigateToSearchServiceCallback(onItemFound); - - await client.TryInvokeAsync( + var result = client.TryInvokeStreamAsync( solution, - (service, solutionInfo, callbackId, cancellationToken) => - service.SearchGeneratedDocumentsAsync(solutionInfo, project.Id, searchPattern, kinds.ToImmutableArray(), callbackId, cancellationToken), - callback, cancellationToken).ConfigureAwait(false); + (service, solutionInfo, cancellationToken) => + service.SearchGeneratedDocumentsAsync(solutionInfo, project.Id, searchPattern, kinds.ToImmutableArray(), cancellationToken), + cancellationToken); - return; + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; } + else + { + var result = SearchGeneratedDocumentsInCurrentProcessAsync( + project, searchPattern, kinds, cancellationToken); - await SearchGeneratedDocumentsInCurrentProcessAsync( - project, searchPattern, kinds, onItemFound, cancellationToken).ConfigureAwait(false); + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; + } } - public static async Task SearchGeneratedDocumentsInCurrentProcessAsync( + public static async IAsyncEnumerable SearchGeneratedDocumentsInCurrentProcessAsync( Project project, string pattern, IImmutableSet kinds, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { // If the user created a dotted pattern then we'll grab the last part of the name var (patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(pattern); @@ -57,7 +60,8 @@ public static async Task SearchGeneratedDocumentsInCurrentProcessAsync( // First generate all the source-gen docs. Then handoff to the standard search routine to find matches in them. var generatedDocs = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - await ProcessDocumentsAsync(searchDocument: null, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onResultFound, generatedDocs.ToSet(), cancellationToken).ConfigureAwait(false); + await foreach (var item in ProcessDocumentsAsync(searchDocument: null, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, generatedDocs.ToSet(), cancellationToken).ConfigureAwait(false)) + yield return item; } } } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs index 69d5e68f494cc..8b2b3355fbb44 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs @@ -7,13 +7,16 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -36,10 +39,10 @@ internal abstract partial class AbstractNavigateToSearchService // Map our value to 'Fuzzy' as that's the lower value the platform supports. (PatternMatchKind.LowercaseSubstring, NavigateToMatchKind.Fuzzy)); - private static async Task SearchProjectInCurrentProcessAsync( + private static async IAsyncEnumerable SearchProjectInCurrentProcessAsync( Project project, ImmutableArray priorityDocuments, Document? searchDocument, string pattern, IImmutableSet kinds, - Func onResultFound, CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { // We're doing a real search over the fully loaded solution now. No need to hold onto the cached map // of potentially stale indices. @@ -52,23 +55,24 @@ private static async Task SearchProjectInCurrentProcessAsync( // Prioritize the active documents if we have any. var highPriDocs = priorityDocuments.Where(d => project.ContainsDocument(d.Id)).ToSet(); - await ProcessDocumentsAsync(searchDocument, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onResultFound, highPriDocs, cancellationToken).ConfigureAwait(false); + await foreach (var item in ProcessDocumentsAsync(searchDocument, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, highPriDocs, cancellationToken).ConfigureAwait(false)) + yield return item; // Then process non-priority documents. var lowPriDocs = project.Documents.Where(d => !highPriDocs.Contains(d)).ToSet(); - await ProcessDocumentsAsync(searchDocument, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onResultFound, lowPriDocs, cancellationToken).ConfigureAwait(false); + await foreach (var item in ProcessDocumentsAsync(searchDocument, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, lowPriDocs, cancellationToken).ConfigureAwait(false)) + yield return item; } - private static async Task ProcessDocumentsAsync( + private static IAsyncEnumerable ProcessDocumentsAsync( Document? searchDocument, string patternName, string? patternContainer, DeclaredSymbolInfoKindSet kinds, - Func onResultFound, ISet documents, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var tasks); + using var _ = ArrayBuilder>.GetInstance(out var builders); foreach (var document in documents) { @@ -76,36 +80,55 @@ private static async Task ProcessDocumentsAsync( continue; cancellationToken.ThrowIfCancellationRequested(); - tasks.Add(Task.Run(() => - ProcessDocumentAsync(document, patternName, patternContainer, kinds, onResultFound, cancellationToken), cancellationToken)); + builders.Add(ProcessDocumentAsync(document, patternName, patternContainer, kinds, cancellationToken)); } - await Task.WhenAll(tasks).ConfigureAwait(false); + return builders.ToImmutable().MergeAsync(cancellationToken); } - private static async Task ProcessDocumentAsync( + private static async IAsyncEnumerable ProcessDocumentAsync( Document document, string patternName, string? patternContainer, DeclaredSymbolInfoKindSet kinds, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var index = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); - await ProcessIndexAsync( - document.Id, document, patternName, patternContainer, kinds, onResultFound, index, cancellationToken).ConfigureAwait(false); + await foreach (var item in ProcessIndexAsync( + document.Id, document, patternName, patternContainer, kinds, index, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } } - private static async Task ProcessIndexAsync( + private static async IAsyncEnumerable ProcessStaleIndexAsync( + IChecksummedPersistentStorageService storageService, + string patternName, + string? patternContainer, + DeclaredSymbolInfoKindSet kinds, + DocumentKey documentKey, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + var index = await GetIndexAsync(storageService, documentKey, cancellationToken).ConfigureAwait(false); + if (index == null) + yield break; + + await foreach (var item in ProcessIndexAsync( + documentKey.Id, document: null, patternName, patternContainer, kinds, index, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } + } + + private static async IAsyncEnumerable ProcessIndexAsync( DocumentId documentId, Document? document, string patternName, string? patternContainer, DeclaredSymbolInfoKindSet kinds, - Func onResultFound, TopLevelSyntaxTreeIndex index, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var containerMatcher = patternContainer != null ? PatternMatcher.CreateDotSeparatedContainerMatcher(patternContainer) @@ -120,22 +143,23 @@ private static async Task ProcessIndexAsync( if (declaredSymbolInfo.Kind == DeclaredSymbolInfoKind.Namespace) continue; - await AddResultIfMatchAsync( + var result = await GetResultIfMatchAsync( documentId, document, declaredSymbolInfo, nameMatcher, containerMatcher, - kinds, onResultFound, cancellationToken).ConfigureAwait(false); + kinds, cancellationToken).ConfigureAwait(false); + if (result != null) + yield return result.Value; } } - private static async Task AddResultIfMatchAsync( + private static async ValueTask GetResultIfMatchAsync( DocumentId documentId, Document? document, DeclaredSymbolInfo declaredSymbolInfo, PatternMatcher nameMatcher, PatternMatcher? containerMatcher, DeclaredSymbolInfoKindSet kinds, - Func onResultFound, CancellationToken cancellationToken) { using var nameMatches = TemporaryArray.Empty; @@ -159,7 +183,11 @@ private static async Task AddResultIfMatchAsync( var result = ConvertResult( documentId, document, declaredSymbolInfo, nameMatches, containerMatches, additionalMatchingProjects); - await onResultFound(result).ConfigureAwait(false); + return result; + } + else + { + return null; } } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 4ef10fe326033..0d574a954b575 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -3,91 +3,95 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Storage; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo { internal abstract partial class AbstractNavigateToSearchService { - public async Task SearchDocumentAsync( + public async IAsyncEnumerable SearchDocumentAsync( Document document, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var solution = document.Project.Solution; - var onItemFound = GetOnItemFoundCallback(solution, activeDocument, onResultFound, cancellationToken); var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client != null) { - var callback = new NavigateToSearchServiceCallback(onItemFound); // Don't need to sync the full solution when searching a particular project. - await client.TryInvokeAsync( + var result = client.TryInvokeStreamAsync( document.Project, - (service, solutionInfo, callbackId, cancellationToken) => - service.SearchDocumentAsync(solutionInfo, document.Id, searchPattern, kinds.ToImmutableArray(), callbackId, cancellationToken), - callback, cancellationToken).ConfigureAwait(false); + (service, solutionInfo, cancellationToken) => + service.SearchDocumentAsync(solutionInfo, document.Id, searchPattern, kinds.ToImmutableArray(), cancellationToken), + cancellationToken); - return; + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; } + else + { + var result = SearchDocumentInCurrentProcessAsync(document, searchPattern, kinds, cancellationToken); - await SearchDocumentInCurrentProcessAsync(document, searchPattern, kinds, onItemFound, cancellationToken).ConfigureAwait(false); + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; + } } - public static Task SearchDocumentInCurrentProcessAsync(Document document, string searchPattern, IImmutableSet kinds, Func onItemFound, CancellationToken cancellationToken) + public static IAsyncEnumerable SearchDocumentInCurrentProcessAsync( + Document document, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { return SearchProjectInCurrentProcessAsync( document.Project, priorityDocuments: ImmutableArray.Empty, document, - searchPattern, kinds, onItemFound, cancellationToken); + searchPattern, kinds, cancellationToken); } - public async Task SearchProjectAsync( + public async IAsyncEnumerable SearchProjectAsync( Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, - Func onResultFound, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var solution = project.Solution; - var onItemFound = GetOnItemFoundCallback(solution, activeDocument, onResultFound, cancellationToken); var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { var priorityDocumentIds = priorityDocuments.SelectAsArray(d => d.Id); - var callback = new NavigateToSearchServiceCallback(onItemFound); - await client.TryInvokeAsync( + var result = client.TryInvokeStreamAsync( solution, - (service, solutionInfo, callbackId, cancellationToken) => - service.SearchProjectAsync(solutionInfo, project.Id, priorityDocumentIds, searchPattern, kinds.ToImmutableArray(), callbackId, cancellationToken), - callback, cancellationToken).ConfigureAwait(false); + (service, solutionInfo, cancellationToken) => + service.SearchProjectAsync(solutionInfo, project.Id, priorityDocumentIds, searchPattern, kinds.ToImmutableArray(), cancellationToken), + cancellationToken); - return; + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; } + else + { + var result = SearchProjectInCurrentProcessAsync(project, priorityDocuments, searchPattern, kinds, cancellationToken); - await SearchProjectInCurrentProcessAsync(project, priorityDocuments, searchPattern, kinds, onItemFound, cancellationToken).ConfigureAwait(false); + await foreach (var item in ConvertItemsAsync(solution, activeDocument, result, cancellationToken).ConfigureAwait(false)) + yield return item; + } } - public static Task SearchProjectInCurrentProcessAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Func onItemFound, CancellationToken cancellationToken) + public static IAsyncEnumerable SearchProjectInCurrentProcessAsync( + Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { return SearchProjectInCurrentProcessAsync( project, priorityDocuments, searchDocument: null, - pattern: searchPattern, kinds, onItemFound, cancellationToken); + pattern: searchPattern, kinds, cancellationToken); } } } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs index 6504a229bd678..a207ee628fca6 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -27,15 +29,18 @@ internal abstract partial class AbstractNavigateToSearchService : INavigateToSea public bool CanFilter => true; - private static Func GetOnItemFoundCallback( - Solution solution, Document? activeDocument, Func onResultFound, CancellationToken cancellationToken) + private static async IAsyncEnumerable ConvertItemsAsync( + Solution solution, + Document? activeDocument, + IAsyncEnumerable items, + [EnumeratorCancellation] CancellationToken cancellationToken) { - return async item => + await foreach (var item in items) { - var result = await item.TryCreateSearchResultAsync(solution, activeDocument, cancellationToken).ConfigureAwait(false); - if (result != null) - await onResultFound(result).ConfigureAwait(false); - }; + var converted = await item.TryCreateSearchResultAsync(solution, activeDocument, cancellationToken).ConfigureAwait(false); + if (converted != null) + yield return converted; + } } } } diff --git a/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs index 42dc5b51bfcc8..9fb97b5bbc930 100644 --- a/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs @@ -2,10 +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; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.NavigateTo @@ -15,7 +14,7 @@ internal interface INavigateToSearchService : ILanguageService IImmutableSet KindsProvided { get; } bool CanFilter { get; } - Task SearchDocumentAsync(Document document, string searchPattern, IImmutableSet kinds, Document? activeDocument, Func onResultFound, CancellationToken cancellationToken); + IAsyncEnumerable SearchDocumentAsync(Document document, string searchPattern, IImmutableSet kinds, Document? activeDocument, CancellationToken cancellationToken); /// /// Searches the documents inside for symbols that matches . @@ -23,18 +22,18 @@ internal interface INavigateToSearchService : ILanguageService /// be used to prioritize work. Generates files should not be searched. Results should be up to date with the actual /// document contents for the requested project. /// - Task SearchProjectAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, Func onResultFound, CancellationToken cancellationToken); + IAsyncEnumerable SearchProjectAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, CancellationToken cancellationToken); /// /// Searches the documents inside for symbols that matches . /// Results should be reported from a previous computed cache (even if that cache is out of date) to produce results as /// quickly as possible. /// - Task SearchCachedDocumentsAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, Func onResultFound, CancellationToken cancellationToken); + IAsyncEnumerable SearchCachedDocumentsAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, Document? activeDocument, CancellationToken cancellationToken); /// /// Searches the generated documents inside for symbols that matches . /// - Task SearchGeneratedDocumentsAsync(Project project, string searchPattern, IImmutableSet kinds, Document? activeDocument, Func onResultFound, CancellationToken cancellationToken); + IAsyncEnumerable SearchGeneratedDocumentsAsync(Project project, string searchPattern, IImmutableSet kinds, Document? activeDocument, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index 533da82fdd1f7..599f55d0a52d0 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -2,68 +2,22 @@ // 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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Storage; namespace Microsoft.CodeAnalysis.NavigateTo { internal interface IRemoteNavigateToSearchService { - ValueTask SearchDocumentAsync(Checksum solutionChecksum, DocumentId documentId, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); - ValueTask SearchProjectAsync(Checksum solutionChecksum, ProjectId projectId, ImmutableArray priorityDocumentIds, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); + IAsyncEnumerable SearchDocumentAsync(Checksum solutionChecksum, DocumentId documentId, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); + IAsyncEnumerable SearchProjectAsync(Checksum solutionChecksum, ProjectId projectId, ImmutableArray priorityDocumentIds, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); - ValueTask SearchGeneratedDocumentsAsync(Checksum solutionChecksum, ProjectId projectId, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); - ValueTask SearchCachedDocumentsAsync(ImmutableArray documentKeys, ImmutableArray priorityDocumentKeys, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); + IAsyncEnumerable SearchGeneratedDocumentsAsync(Checksum solutionChecksum, ProjectId projectId, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); + IAsyncEnumerable SearchCachedDocumentsAsync(ImmutableArray documentKeys, ImmutableArray priorityDocumentKeys, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); ValueTask HydrateAsync(Checksum solutionChecksum, CancellationToken cancellationToken); - - public interface ICallback - { - ValueTask OnResultFoundAsync(RemoteServiceCallbackId callbackId, RoslynNavigateToItem result); - } - } - - [ExportRemoteServiceCallbackDispatcher(typeof(IRemoteNavigateToSearchService)), Shared] - internal sealed class NavigateToSearchServiceServerCallbackDispatcher : RemoteServiceCallbackDispatcher, IRemoteNavigateToSearchService.ICallback - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public NavigateToSearchServiceServerCallbackDispatcher() - { - } - - private new NavigateToSearchServiceCallback GetCallback(RemoteServiceCallbackId callbackId) - => (NavigateToSearchServiceCallback)base.GetCallback(callbackId); - - public ValueTask OnResultFoundAsync(RemoteServiceCallbackId callbackId, RoslynNavigateToItem result) - => GetCallback(callbackId).OnResultFoundAsync(result); - } - - internal sealed class NavigateToSearchServiceCallback - { - private readonly Func _onResultFound; - - public NavigateToSearchServiceCallback(Func onResultFound) - { - _onResultFound = onResultFound; - } - - public async ValueTask OnResultFoundAsync(RoslynNavigateToItem result) - { - try - { - await _onResultFound(result).ConfigureAwait(false); - } - catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex)) - { - } - } } } diff --git a/src/Features/Core/Portable/NavigateTo/NavigateToSearcher.cs b/src/Features/Core/Portable/NavigateTo/NavigateToSearcher.cs index 7af65e2baa273..3525f71d399c2 100644 --- a/src/Features/Core/Portable/NavigateTo/NavigateToSearcher.cs +++ b/src/Features/Core/Portable/NavigateTo/NavigateToSearcher.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -20,6 +21,8 @@ namespace Microsoft.CodeAnalysis.NavigateTo { + using NavigateToResultStream = IAsyncEnumerable<(Project project, INavigateToSearchResult result)>; + [Flags] internal enum NavigateToSearchScope { @@ -147,10 +150,11 @@ private async Task SearchCurrentDocumentAsync(CancellationToken cancellationToke return; await AddProgressItemsAsync(1, cancellationToken).ConfigureAwait(false); - await service.SearchDocumentAsync( - _activeDocument, _searchPattern, _kinds, _activeDocument, - r => _callback.AddItemAsync(project, r, cancellationToken), - cancellationToken).ConfigureAwait(false); + await foreach (var item in service.SearchDocumentAsync( + _activeDocument, _searchPattern, _kinds, _activeDocument, cancellationToken)) + { + await _callback.AddItemAsync(project, item, cancellationToken).ConfigureAwait(false); + } } private async Task SearchAllProjectsAsync( @@ -175,10 +179,10 @@ await AddProgressItemsAsync( cancellationToken).ConfigureAwait(false); if (searchRegularDocuments) - await SearchFullyLoadedProjectsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); + await ProcessResultsAsync(seenItems, SearchFullyLoadedProjectsAsync(orderedProjects, cancellationToken), cancellationToken).ConfigureAwait(false); if (searchGeneratedDocuments) - await SearchGeneratedDocumentsAsync(seenItems, cancellationToken).ConfigureAwait(false); + await ProcessResultsAsync(seenItems, SearchGeneratedDocumentsAsync(cancellationToken), cancellationToken).ConfigureAwait(false); } else { @@ -191,14 +195,16 @@ await AddProgressItemsAsync( projectCount * 2, cancellationToken).ConfigureAwait(false); - await SearchCachedDocumentsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); + await ProcessResultsAsync( + seenItems, SearchCachedDocumentsAsync(orderedProjects, cancellationToken), cancellationToken).ConfigureAwait(false); // If searching cached data returned any results, then we're done. We've at least shown some results // to the user. That will hopefully serve them well enough until the solution fully loads. if (seenItems.Count > 0) return; - await SearchFullyLoadedProjectsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); + await ProcessResultsAsync( + seenItems, SearchFullyLoadedProjectsAsync(orderedProjects, cancellationToken), cancellationToken).ConfigureAwait(false); // Report a telemetry event to track if we found uncached items after failing to find cached items. // In practice if we see that we are always finding uncached items, then it's likely something @@ -209,6 +215,23 @@ await AddProgressItemsAsync( } } + private async Task ProcessResultsAsync( + HashSet seenItems, + NavigateToResultStream results, + CancellationToken cancellationToken) + { + await foreach (var (project, result) in results) + { + // If we're seeing a dupe in another project, then filter it out here. The results from + // the individual projects will already contain the information about all the projects + // leading to a better condensed view that doesn't look like it contains duplicate info. + if (!seenItems.Add(result)) + continue; + + await _callback.AddItemAsync(project, result, cancellationToken).ConfigureAwait(false); + } + } + /// /// Returns a sequence of groups of projects to process. The sequence is in priority order, and all projects in /// a particular group should be processed before the next group. This allows us to associate CPU resources in @@ -276,12 +299,11 @@ private ImmutableArray GetPriorityDocuments(Project project) return result.ToImmutable(); } - private async Task ProcessOrderedProjectsAsync( + private async NavigateToResultStream ProcessOrderedProjectsAsync( bool parallel, ImmutableArray> orderedProjects, - HashSet seenItems, - Func, Task> processProjectAsync, - CancellationToken cancellationToken) + Func> processProjectAsync, + [EnumeratorCancellation] CancellationToken cancellationToken) { // Process each group one at a time. However, in each group process all projects in parallel to get results // as quickly as possible. The net effect of this is that we will search the active doc immediately, then @@ -293,18 +315,22 @@ private async Task ProcessOrderedProjectsAsync( if (!parallel) { foreach (var project in projectGroup) - await SearchCoreAsync(project).ConfigureAwait(false); + { + await foreach (var pair in SearchCoreAsync(project).ConfigureAwait(false)) + yield return pair; + } } else { - var allTasks = projectGroup.Select(p => Task.Run(() => SearchCoreAsync(p), cancellationToken)); - await Task.WhenAll(allTasks).ConfigureAwait(false); + var streams = projectGroup.SelectAsArray(SearchCoreAsync); + await foreach (var pair in streams.MergeAsync(cancellationToken).ConfigureAwait(false)) + yield return pair; } } - return; + yield break; - async Task SearchCoreAsync(Project project) + async NavigateToResultStream SearchCoreAsync(Project project) { try { @@ -312,21 +338,10 @@ async Task SearchCoreAsync(Project project) // complete search. That way we don't call back into this project ever. var service = _host.GetNavigateToSearchService(project); if (service == null) - return; + yield break; - await processProjectAsync(service, project, result => - { - // If we're seeing a dupe in another project, then filter it out here. The results from - // the individual projects will already contain the information about all the projects - // leading to a better condensed view that doesn't look like it contains duplicate info. - lock (seenItems) - { - if (!seenItems.Add(result)) - return Task.CompletedTask; - } - - return _callback.AddItemAsync(project, result, cancellationToken); - }).ConfigureAwait(false); + await foreach (var result in processProjectAsync(service, project).ConfigureAwait(false)) + yield return (project, result); } finally { @@ -336,9 +351,8 @@ await processProjectAsync(service, project, result => } } - private Task SearchFullyLoadedProjectsAsync( + private NavigateToResultStream SearchFullyLoadedProjectsAsync( ImmutableArray> orderedProjects, - HashSet seenItems, CancellationToken cancellationToken) { // Search the fully loaded project in parallel. We know this will be called after we've already hydrated the @@ -348,14 +362,12 @@ private Task SearchFullyLoadedProjectsAsync( return ProcessOrderedProjectsAsync( parallel: true, orderedProjects, - seenItems, - (s, p, cb) => s.SearchProjectAsync(p, GetPriorityDocuments(p), _searchPattern, _kinds, _activeDocument, cb, cancellationToken), + (s, p) => s.SearchProjectAsync(p, GetPriorityDocuments(p), _searchPattern, _kinds, _activeDocument, cancellationToken), cancellationToken); } - private Task SearchCachedDocumentsAsync( + private NavigateToResultStream SearchCachedDocumentsAsync( ImmutableArray> orderedProjects, - HashSet seenItems, CancellationToken cancellationToken) { // We search cached information in parallel. This is because there's no syncing step when searching cached @@ -365,14 +377,11 @@ private Task SearchCachedDocumentsAsync( return ProcessOrderedProjectsAsync( parallel: true, orderedProjects, - seenItems, - (s, p, cb) => s.SearchCachedDocumentsAsync(p, GetPriorityDocuments(p), _searchPattern, _kinds, _activeDocument, cb, cancellationToken), + (s, p) => s.SearchCachedDocumentsAsync(p, GetPriorityDocuments(p), _searchPattern, _kinds, _activeDocument, cancellationToken), cancellationToken); } - private Task SearchGeneratedDocumentsAsync( - HashSet seenItems, - CancellationToken cancellationToken) + private NavigateToResultStream SearchGeneratedDocumentsAsync(CancellationToken cancellationToken) { // Process all projects, serially, in topological order. Generating source can be expensive. It requires // creating and processing the entire compilation for a project, which itself may require dependent compilations @@ -393,8 +402,7 @@ private Task SearchGeneratedDocumentsAsync( return ProcessOrderedProjectsAsync( parallel: false, allProjects, - seenItems, - (s, p, cb) => s.SearchGeneratedDocumentsAsync(p, _searchPattern, _kinds, _activeDocument, cb, cancellationToken), + (s, p) => s.SearchGeneratedDocumentsAsync(p, _searchPattern, _kinds, _activeDocument, cancellationToken), cancellationToken); } } diff --git a/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs b/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs index 205a31703cc70..b918254361b47 100644 --- a/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs +++ b/src/Features/Core/Portable/NavigateTo/RoslynNavigateToItem.cs @@ -72,7 +72,7 @@ public RoslynNavigateToItem( NameMatchSpans = nameMatchSpans; } - public async Task TryCreateSearchResultAsync( + public async ValueTask TryCreateSearchResultAsync( Solution solution, Document? activeDocument, CancellationToken cancellationToken) { if (IsStale) diff --git a/src/Features/Core/Portable/Navigation/ICrossLanguageSymbolNavigationService.cs b/src/Features/Core/Portable/Navigation/ICrossLanguageSymbolNavigationService.cs index 1d86b3032dd6a..cb76659f24f94 100644 --- a/src/Features/Core/Portable/Navigation/ICrossLanguageSymbolNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/ICrossLanguageSymbolNavigationService.cs @@ -18,6 +18,9 @@ internal interface ICrossLanguageSymbolNavigationService /// defined at: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#id-strings. Should /// return if the 3rd party language cannot navigate to this particular symbol. /// - Task TryGetNavigableLocationAsync(string documentationCommentId, CancellationToken cancellationToken); + /// The name of the assembly the symbol was defined in. Can be used by the + /// receiver to quickly filter down to the project/compilation search for the symbol. + Task TryGetNavigableLocationAsync( + string assemblyName, string documentationCommentId, CancellationToken cancellationToken); } } 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 ce26e8be52705..49b3c6720d095 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -3,10 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection.Metadata.Ecma335; @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Structure; @@ -37,10 +36,27 @@ internal sealed class PdbSourceDocumentMetadataAsSourceFileProvider : IMetadataA private readonly IImplementationAssemblyLookupService _implementationAssemblyLookupService; private readonly IPdbSourceDocumentLogger? _logger; + /// + /// Accessed only in and , both of which + /// are called under a lock in . So this is safe as a plain + /// dictionary. + /// private readonly Dictionary _assemblyToProjectMap = new(); - private readonly Dictionary _fileToDocumentInfoMap = new(); + + /// + /// Accessed only in and , both of which + /// are called under a lock in . So this is safe as a plain + /// set. + /// private readonly HashSet _sourceLinkEnabledProjects = new(); + /// + /// Accessed both in and in UI thread operations. Those should not + /// generally run concurrently. However, to be safe, we make this a concurrent dictionary to be safe to that + /// potentially happening. + /// + private readonly ConcurrentDictionary _fileToDocumentInfoMap = new(); + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public PdbSourceDocumentMetadataAsSourceFileProvider( @@ -172,19 +188,23 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( } 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); } 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); + var projectInfo = CreateProjectInfo(metadataWorkspace, sourceProject, pdbCompilationOptions, assemblyName, assemblyVersion, projectChecksumAlgorithm); if (projectInfo is null) return null; @@ -227,28 +247,33 @@ public PdbSourceDocumentMetadataAsSourceFileProvider( navigateProject = metadataWorkspace.CurrentSolution.GetRequiredProject(projectId); } + // If MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync can't find the actual document to navigate to, it will fall back + // to the document passed in, which we just use the first document for. // TODO: Support results from multiple source files: https://github.com/dotnet/roslyn/issues/55834 - var firstSourceFileInfo = sourceFileInfos[0]!; - var documentPath = firstSourceFileInfo.FilePath; - var document = navigateProject.Documents.FirstOrDefault(d => d.FilePath?.Equals(documentPath, StringComparison.OrdinalIgnoreCase) ?? false); + var firstDocumentFilePath = sourceFileInfos[0]!.FilePath; + var firstDocument = navigateProject.Documents.FirstOrDefault(d => d.FilePath?.Equals(firstDocumentFilePath, StringComparison.OrdinalIgnoreCase) ?? false); + var navigateLocation = await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, firstDocument, cancellationToken).ConfigureAwait(false); - var navigateLocation = await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, document, cancellationToken).ConfigureAwait(false); + // In the case of partial classes, finding the location in the generated source may return a location in a different document, so we + // have to make sure to look it up again. var navigateDocument = navigateProject.GetDocument(navigateLocation.SourceTree); + Contract.ThrowIfNull(navigateDocument); + var sourceDescription = sourceFileInfos.FirstOrDefault(sfi => sfi!.FilePath?.Equals(navigateDocument.FilePath, StringComparison.OrdinalIgnoreCase) ?? false)?.SourceDescription ?? FeaturesResources.from_metadata; var documentName = string.Format( "{0} [{1}]", - navigateDocument!.Name, - firstSourceFileInfo.SourceDescription); - var documentTooltip = sourceDocuments[0].FilePath + Environment.NewLine + dllPath; + navigateDocument.Name, + sourceDescription); + var documentTooltip = navigateDocument.FilePath + Environment.NewLine + dllPath; - return new MetadataAsSourceFile(documentPath, navigateLocation, documentName, documentTooltip); + 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; @@ -262,11 +287,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 @@ -294,9 +322,11 @@ 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 @@ -306,40 +336,45 @@ 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(); } - public bool ShouldCollapseOnOpen(string filePath, BlockStructureOptions blockStructureOptions) + private static void AssertIsMainThread(MetadataAsSourceWorkspace workspace) { - if (_fileToDocumentInfoMap.TryGetValue(filePath, out _)) - { - return blockStructureOptions.CollapseMetadataImplementationsWhenFirstOpened; - } + Contract.ThrowIfNull(workspace); + var threadingService = workspace.Services.GetRequiredService().Service; + Contract.ThrowIfFalse(threadingService.IsOnMainThread); + } - return false; + public bool ShouldCollapseOnOpen(MetadataAsSourceWorkspace workspace, string filePath, BlockStructureOptions blockStructureOptions) + { + AssertIsMainThread(workspace); + return _fileToDocumentInfoMap.TryGetValue(filePath, out _) && blockStructureOptions.CollapseMetadataImplementationsWhenFirstOpened; } - public bool TryAddDocumentToWorkspace(Workspace workspace, string filePath, SourceTextContainer sourceTextContainer) + public bool TryAddDocumentToWorkspace(MetadataAsSourceWorkspace workspace, string filePath, SourceTextContainer sourceTextContainer) { + AssertIsMainThread(workspace); + if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { workspace.OnDocumentOpened(info.DocumentId, sourceTextContainer); - return true; } return false; } - public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) + public bool TryRemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, string filePath) { + AssertIsMainThread(workspace); + 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; } @@ -372,16 +407,10 @@ public bool TryRemoveDocumentFromWorkspace(Workspace workspace, string filePath) return null; } - public void CleanupGeneratedFiles(Workspace? workspace) + public void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace) { - if (workspace is not null) - { - var projectIds = _assemblyToProjectMap.Values; - foreach (var projectId in projectIds) - { - workspace.OnProjectRemoved(projectId); - } - } + foreach (var projectId in _assemblyToProjectMap.Values) + workspace.OnProjectRemoved(projectId); _assemblyToProjectMap.Clear(); @@ -392,7 +421,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/Shared/Extensions/DocumentExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs index 62af6c97f0b94..490032925b944 100644 --- a/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs @@ -119,7 +119,7 @@ public static async Task GetApplicableNamingRuleAsync(this Document return rule; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public static async Task GetApplicableNamingRuleAsync( @@ -132,7 +132,7 @@ public static async Task GetApplicableNamingRuleAsync( return rule; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public static async Task GetApplicableNamingRuleAsync( @@ -145,7 +145,7 @@ public static async Task GetApplicableNamingRuleAsync( return rule; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/Core/Portable/Snippets/SnippetPlaceholder.cs b/src/Features/Core/Portable/Snippets/SnippetPlaceholder.cs index 5932bdfeff17d..a60fd6401d9cc 100644 --- a/src/Features/Core/Portable/Snippets/SnippetPlaceholder.cs +++ b/src/Features/Core/Portable/Snippets/SnippetPlaceholder.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Snippets { diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs index 0f7f010fb9e85..058409c7c67c3 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs @@ -3,12 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs index f744d28ce15c8..a78a9ccd6b818 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/ISnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/ISnippetProvider.cs index a4205fba2106c..bbf1858de9980 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/ISnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/ISnippetProvider.cs @@ -2,13 +2,8 @@ // 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.Host; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders { diff --git a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs index c8fed27f51909..f8a65a1d9d0ba 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs @@ -73,7 +73,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS if (!oldDocument.TryGetTopLevelChangeTextVersion(out var oldTopLevelChangeVersion) || !newDocument.TryGetTopLevelChangeTextVersion(out var newTopLevelChangeVersion)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // quicker common case @@ -96,7 +96,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs index 066ef9ff0c4a2..23189dd5b54bc 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using System.Transactions; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; @@ -147,6 +148,10 @@ private async Task ProcessAsync() { // ignore cancellation exception } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e)) + { + // In case any error happen during the execution, don't exit the loop and continue to work on the next item. + } } } diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs index 54b3d0c2b3463..28082a32b9946 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs @@ -207,7 +207,7 @@ public static void LogIncrementalAnalyzerProcessorStatistics(int correlationId, } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs index 18a1e2e3aba98..7ff970f9d9df5 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs @@ -40,7 +40,7 @@ public SolutionCrawlerRegistrationService( AssertAnalyzerProviders(_analyzerProviders); _documentWorkCoordinatorMap = new Dictionary(ReferenceEqualityComparer.Instance); - _listener = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler); + _listener = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy); } public void Register(Workspace workspace) diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs index 4c1868943b046..8d1a0561f25d1 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs @@ -62,7 +62,7 @@ protected override bool TryTakeAnyWork_NoLock( return true; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private DocumentId GetBestDocumentId_NoLock( diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs index 7b946959668e3..584f6c839caca 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs @@ -64,7 +64,7 @@ protected override bool TryTakeAnyWork_NoLock( return true; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override bool AddOrReplace_NoLock(WorkItem item) diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs index 2126979f4ae36..1a2d37c892f95 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs @@ -285,7 +285,7 @@ protected ProjectId GetBestProjectId_NoLock( return pair.Key; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs index 747d0dc739c83..f229bbdd56550 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs @@ -150,7 +150,7 @@ protected override async Task ExecuteAsync() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } finally { @@ -209,7 +209,7 @@ private async Task ProcessDocumentAsync(Solution solution, ImmutableArray anal } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } finally { diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index cdef160379d96..8c2b26f6cc458 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -175,7 +175,7 @@ protected override async Task ExecuteAsync() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } finally { @@ -285,7 +285,7 @@ private async Task TryProcessOneHigherPriorityDocumentAsync() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -368,7 +368,7 @@ private async Task ProcessDocumentAsync(ImmutableArray ana } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } finally { @@ -468,7 +468,7 @@ private async Task ProcessReanalyzeDocumentAsync(ImmutableArray TryDownloadFileAsync(IFileDownloader fileDownloader XmlResolver = null }; - using var reader = XmlReader.Create(stream, settings); + // This code must always succeed. If it does not, that means that either the server reported bogus data + // to the file-downloader, or the file-downloader is serving us bogus data. In other event, there is + // something wrong with those components, and we should both report the issue to watson, and stop doing + // the update. + try + { + using var reader = XmlReader.Create(stream, settings); - var result = XElement.Load(reader); - await LogInfoAsync("Converting data to XElement completed", cancellationToken).ConfigureAwait(false); - return result; + var result = XElement.Load(reader); + await LogInfoAsync("Converting data to XElement completed", cancellationToken).ConfigureAwait(false); + return result; + } + catch (Exception e) when (ReportAndDoNotCatch(e)) + { + throw ExceptionUtilities.Unreachable(); + } + + bool ReportAndDoNotCatch(Exception exception) + { + // Directly report issue here so we can collect a dump that indicates precisely what is in the + // stream. Then return 'false' explicitly so that the catch handler doesn't run and the exception + // properly tears down the update. + _service._reportAndSwallowExceptionUnlessCanceled(exception, cancellationToken); + return false; + } } private async Task RepeatIOAsync(Func action, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs b/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs index 815b8568aa915..01c1f97c9a456 100644 --- a/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs +++ b/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs @@ -10,9 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.TaskList; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.TodoComments; namespace Microsoft.CodeAnalysis.TaskList { diff --git a/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs b/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs deleted file mode 100644 index 3d5eb1bf035c2..0000000000000 --- a/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs +++ /dev/null @@ -1,73 +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.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.TodoComments -{ - /// - /// A TODO comment that has been found within the user's code. - /// - [Obsolete($"Use {nameof(TaskListItem)} instead")] - internal readonly struct TodoComment - { - public TodoCommentDescriptor Descriptor { get; } - public string Message { get; } - public int Position { get; } - - public TodoComment(TodoCommentDescriptor descriptor, string message, int position) : this() - { - Descriptor = descriptor; - Message = message; - Position = position; - } - - private TaskListItem CreateTaskListItem( - Document document, SourceText text, SyntaxTree? tree) - { - // make sure given position is within valid text range. - var textSpan = new TextSpan(Math.Min(text.Length, Math.Max(0, Position)), 0); - - var location = tree == null - ? Location.Create(document.FilePath!, textSpan, text.Lines.GetLinePositionSpan(textSpan)) - : tree.GetLocation(textSpan); - - return new TaskListItem( - Descriptor.Priority, - Message, - document.Id, - location.GetLineSpan(), - location.GetMappedLineSpan()); - } - - public static async ValueTask> ConvertAsync( - Document document, - ImmutableArray todoComments, - CancellationToken cancellationToken) - { - if (todoComments.Length == 0) - return ImmutableArray.Empty; - - var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - - return todoComments.SelectAsArray(comment => comment.CreateTaskListItem(document, sourceText, syntaxTree)); - } - } - - [Obsolete($"Use {nameof(ITaskListService)} instead")] - internal interface ITodoCommentService : ILanguageService - { - [Obsolete($"Implement {nameof(ITaskListService.GetTaskListItemsAsync)} instead")] - Task> GetTodoCommentsAsync(Document document, ImmutableArray commentDescriptors, CancellationToken cancellationToken); - } -} diff --git a/src/Features/Core/Portable/TodoComments/TodoCommentDescriptor.cs b/src/Features/Core/Portable/TodoComments/TodoCommentDescriptor.cs deleted file mode 100644 index 72ec480123575..0000000000000 --- a/src/Features/Core/Portable/TodoComments/TodoCommentDescriptor.cs +++ /dev/null @@ -1,33 +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.Immutable; -using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.TaskList; - -namespace Microsoft.CodeAnalysis.TodoComments -{ - /// - /// Description of a TODO comment type to find in a user's comments. - /// - [DataContract] - [Obsolete($"Use {nameof(TaskListItemDescriptor)} instead")] - internal readonly struct TodoCommentDescriptor - { - [DataMember(Order = 0)] - public string Text { get; } - [DataMember(Order = 1)] - public int Priority { get; } - - public TodoCommentDescriptor(string text, int priority) - { - Text = text; - Priority = priority; - } - - public static ImmutableArray Parse(ImmutableArray items) - => TaskListItemDescriptor.Parse(items).SelectAsArray(d => new TodoCommentDescriptor(d.Text, d.Priority)); - } -} 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/Core/Portable/Wrapping/AbstractWrapper.cs b/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs index 600295aee2041..0951e67ab31bd 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs @@ -47,7 +47,7 @@ protected static async Task ContainsUnformattableContentAsync( var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); foreach (var item in nodesAndTokens) { - if (item == null || item.Span.IsEmpty) + if (item == null || item.Span.IsEmpty || item.IsMissing) return true; var firstToken = item.IsToken ? item.AsToken() : item.AsNode()!.GetFirstToken(); diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs index 22f34e39b9b84..be0386b9b5036 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Indentation; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Wrapping.SeparatedSyntaxList @@ -43,6 +42,8 @@ protected AbstractSeparatedSyntaxListWrapper(IIndentationService indentationServ protected abstract bool ShouldMoveCloseBraceToNewLine { get; } protected abstract bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options); + protected abstract SyntaxToken FirstToken(TListSyntax listSyntax); + protected abstract SyntaxToken LastToken(TListSyntax listSyntax); protected abstract TListSyntax? TryGetApplicableList(SyntaxNode node); protected abstract SeparatedSyntaxList GetListItems(TListSyntax listSyntax); protected abstract bool PositionIsApplicable( @@ -55,6 +56,12 @@ protected abstract bool PositionIsApplicable( if (listSyntax == null || listSyntax.Span.IsEmpty) return null; + var firstToken = FirstToken(listSyntax); + var lastToken = LastToken(listSyntax); + + if (firstToken.IsMissing || lastToken.IsMissing || firstToken.Span.IsEmpty || lastToken.Span.IsEmpty) + return null; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (!PositionIsApplicable(root, position, declaration, containsSyntaxError, listSyntax)) return null; diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/CapabilitiesManager.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/CapabilitiesManager.cs new file mode 100644 index 0000000000000..9fa2db348e9bb --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/CapabilitiesManager.cs @@ -0,0 +1,46 @@ +// 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 Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; + +internal class CapabilitiesManager : IInitializeManager +{ + private InitializeParams? _initializeParams; + + public void SetInitializeParams(InitializeParams request) + { + _initializeParams = request; + } + + public InitializeResult GetInitializeResult() + { + var serverCapabilities = new ServerCapabilities() + { + SemanticTokensOptions = new SemanticTokensOptions + { + Range = true, + }, + }; + + var initializeResult = new InitializeResult + { + Capabilities = serverCapabilities, + }; + + return initializeResult; + } + + public InitializeParams GetInitializeParams() + { + if (_initializeParams is null) + { + throw new ArgumentNullException(nameof(_initializeParams)); + } + + return _initializeParams; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs new file mode 100644 index 0000000000000..4dc34216742cb --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs @@ -0,0 +1,53 @@ +// 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.CommonLanguageServerProtocol.Framework.Handlers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; + +public class ExampleLanguageServer : AbstractLanguageServer +{ + public ExampleLanguageServer(JsonRpc jsonRpc, ILspLogger logger) : base(jsonRpc, logger) + { + // This spins up the queue and ensure the LSP is ready to start receiving requests + Initialize(); + } + + protected override ILspServices ConstructLspServices() + { + var serviceCollection = new ServiceCollection(); + + var _ = AddHandlers(serviceCollection) + .AddSingleton(_logger) + .AddSingleton, ExampleRequestContextFactory>() + .AddSingleton, CapabilitiesManager>() + .AddSingleton(this); + + var lifeCycleManager = GetLifeCycleManager(); + if (lifeCycleManager != null) + { + serviceCollection.AddSingleton(lifeCycleManager); + } + + var lspServices = new ExampleLspServices(serviceCollection); + + return lspServices; + } + + protected virtual ILifeCycleManager? GetLifeCycleManager() + { + return null; + } + + private static IServiceCollection AddHandlers(IServiceCollection serviceCollection) + { + _ = serviceCollection + .AddSingleton>() + .AddSingleton>(); + return serviceCollection; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLspServices.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLspServices.cs new file mode 100644 index 0000000000000..ff2a70cbba821 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLspServices.cs @@ -0,0 +1,63 @@ +// 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.Collections.Immutable; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using System; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; + +internal class ExampleLspServices : ILspServices +{ + private readonly IServiceProvider _serviceProvider; + + public ExampleLspServices(IServiceCollection serviceCollection) + { + _ = serviceCollection.AddSingleton(this); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + _serviceProvider = serviceProvider; + } + + public T GetRequiredService() where T : notnull + { + var service = _serviceProvider.GetRequiredService(); + + return service; + } + + public object? TryGetService(Type type) + { + var obj = _serviceProvider.GetService(type); + + return obj; + } + + public IEnumerable GetServices() + { + return _serviceProvider.GetServices(); + } + + public void Dispose() + { + } + + public IEnumerable GetRequiredServices() + { + var services = _serviceProvider.GetServices(); + + return services; + } + + public ImmutableArray GetRegisteredServices() + { + throw new NotImplementedException(); + } + + public bool SupportsGetRegisteredServices() + { + return false; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContext.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContext.cs new file mode 100644 index 0000000000000..d1aab02c05235 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContext.cs @@ -0,0 +1,17 @@ +// 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.CommonLanguageServerProtocol.Framework; + +public class ExampleRequestContext +{ + public ILspServices LspServices; + public ILspLogger Logger; + + public ExampleRequestContext(ILspServices lspServices, ILspLogger logger) + { + LspServices = lspServices; + Logger = logger; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContextFactory.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContextFactory.cs new file mode 100644 index 0000000000000..dadeeb9b234b0 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleRequestContextFactory.cs @@ -0,0 +1,27 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; + +internal class ExampleRequestContextFactory : IRequestContextFactory +{ + private readonly ILspServices _lspServices; + + public ExampleRequestContextFactory(ILspServices lspServices) + { + _lspServices = lspServices; + } + + public Task CreateRequestContextAsync(IQueueItem queueItem, TRequestParam param, CancellationToken cancellationToken) + { + var logger = _lspServices.GetRequiredService(); + + var requestContext = new ExampleRequestContext(_lspServices, logger); + + return Task.FromResult(requestContext); + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj new file mode 100644 index 0000000000000..65dda189cc9b6 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj @@ -0,0 +1,20 @@ + + + + + Library + netcoreapp3.1;netstandard2.0 + false + false + An example implementation of the Common Language Server Protocol Framework. + + + + + + + + + + + diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/ExampleTests.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/ExampleTests.cs new file mode 100644 index 0000000000000..53a3e7345be9c --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/ExampleTests.cs @@ -0,0 +1,49 @@ +// 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.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Nerdbank.Streams; +using StreamJsonRpc; +using Xunit; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +public partial class ExampleTests +{ + [Fact] + public async Task InitializeServer_SerializesCorrectly() + { + var logger = GetLogger(); + var server = TestExampleLanguageServer.CreateLanguageServer(logger); + + var result = await server.InitializeServerAsync(); + Assert.True(result.Capabilities.SemanticTokensOptions!.Range!.Value.First); + } + + [Fact] + public async Task ShutdownServer_Succeeds() + { + var logger = GetLogger(); + var server = TestExampleLanguageServer.CreateLanguageServer(logger); + + _ = await server.InitializeServerAsync(); + + await server.ShutdownServerAsync(); + + var result = await server.WaitForShutdown(); + Assert.True(0 == result, "Server failed to shut down properly"); + } + + private static ILspLogger GetLogger() + { + return NoOpLspLogger.Instance; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/HandlerProviderTests.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/HandlerProviderTests.cs new file mode 100644 index 0000000000000..f423df0014e20 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/HandlerProviderTests.cs @@ -0,0 +1,123 @@ +// 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.CommonLanguageServerProtocol.Framework; +using Xunit; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +public partial class HandlerProviderTests +{ + private const string _method = "SomeMethod"; + private const string _wrongMethod = "WrongMethod"; + private static readonly Type _requestType = typeof(int); + private static readonly Type _responseType = typeof(string); + private static readonly Type _wrongResponseType = typeof(long); + + private static readonly IMethodHandler _expectedMethodHandler = new TestMethodHandler(); + + [Fact] + public void GetMethodHandler_ViaGetRequiredServices_Succeeds() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: false); + + var methodHander = handlerProvider.GetMethodHandler(_method, _requestType, _responseType); + + Assert.Same(_expectedMethodHandler, methodHander); + } + + [Fact] + public void GetMethodHandler_ViaGetRegisteredServices_Succeeds() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: true); + + var methodHander = handlerProvider.GetMethodHandler(_method, _requestType, _responseType); + + Assert.Same(_expectedMethodHandler, methodHander); + } + + [Fact] + public void GetMethodHandler_WrongMethod_Throws() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: false); + + Assert.Throws(() => handlerProvider.GetMethodHandler(_wrongMethod, _requestType, _responseType)); + } + + [Fact] + public void GetMethodHandler_WrongResponseType_Throws() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: false); + + Assert.Throws(() => handlerProvider.GetMethodHandler(_method, _requestType, _wrongResponseType)); + } + + [Fact] + public void GetRegisteredMethods_GetRequiredServices() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: false); + + var registeredMethods = handlerProvider.GetRegisteredMethods(); + + Assert.Collection(registeredMethods, + (r) => Assert.Equal(_method, r.MethodName)); + } + + [Fact] + public void GetRegisteredMethods_GetRegisteredServices() + { + var handlerProvider = GetHandlerProvider(supportsGetRegisteredServices: true); + + var registeredMethods = handlerProvider.GetRegisteredMethods(); + + Assert.Collection(registeredMethods, + (r) => Assert.Equal(_method, r.MethodName)); + } + + private static HandlerProvider GetHandlerProvider(bool supportsGetRegisteredServices) + { + var lspServices = GetLspServices(supportsGetRegisteredServices); + var handler = new HandlerProvider(lspServices); + + return handler; + } + + private static ILspServices GetLspServices(bool supportsGetRegisteredServices) + { + var services = new List<(Type, object)> { (typeof(IMethodHandler), _expectedMethodHandler) }; + var lspServices = new TestLspServices(services, supportsGetRegisteredServices); + return lspServices; + } + + [LanguageServerEndpoint(_method)] + internal class TestMethodHandler : IRequestHandler + { + public bool MutatesSolutionState => true; + + public static string Method = _method; + + public static Type RequestType = typeof(int); + + public static Type ResponseType = typeof(string); + + public Task HandleRequestAsync(int request, TestRequestContext context, CancellationToken cancellationToken) + { + return Task.FromResult("stuff"); + } + } + + private class TestMethodHandlerWithoutAttribute : INotificationHandler + { + public bool MutatesSolutionState => true; + + public Task HandleNotificationAsync(TestRequestContext requestContext, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests.csproj b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests.csproj new file mode 100644 index 0000000000000..f101d69d200e4 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests.csproj @@ -0,0 +1,16 @@ + + + + + + net472 + Library + Microsoft.CommonLanguageServerProtocol.Framework.UnitTests + UnitTest + + + + + + + \ No newline at end of file diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/NoOpLspLogger.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/NoOpLspLogger.cs new file mode 100644 index 0000000000000..f833d793e03ee --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/NoOpLspLogger.cs @@ -0,0 +1,39 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +public class NoOpLspLogger : ILspLogger +{ + public static NoOpLspLogger Instance = new NoOpLspLogger(); + + public void LogError(string message, params object[] @params) + { + } + + public void LogException(Exception exception, string? message = null, params object[] @params) + { + } + + public void LogInformation(string message, params object[] @params) + { + } + + public void LogStartContext(string context, params object[] @params) + { + } + + public void LogEndContext(string context, params object[] @params) + { + } + + public void LogWarning(string message, params object[] @params) + { + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs new file mode 100644 index 0000000000000..220532b62b9bc --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs @@ -0,0 +1,130 @@ +// 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.Elfie.Diagnostics; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Moq; +using Nerdbank.Streams; +using StreamJsonRpc; +using Xunit; +using static Microsoft.CommonLanguageServerProtocol.Framework.UnitTests.HandlerProviderTests; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +public class RequestExecutionQueueTests +{ + private class MockServer : AbstractLanguageServer + { + public MockServer() : base(new JsonRpc(new HeaderDelimitedMessageHandler(FullDuplexStream.CreatePair().Item1)), NoOpLspLogger.Instance) + { + } + + protected override ILspServices ConstructLspServices() + { + throw new NotImplementedException(); + } + } + + private const string MethodName = "SomeMethod"; + + private static RequestExecutionQueue GetRequestExecutionQueue(IMethodHandler? methodHandler = null) + { + var handlerProvider = new Mock(MockBehavior.Strict); + var handler = methodHandler ?? GetTestMethodHandler(); + handlerProvider.Setup(h => h.GetMethodHandler(MethodName, TestMethodHandler.RequestType, TestMethodHandler.ResponseType)).Returns(handler); + + var executionQueue = new RequestExecutionQueue(new MockServer(), NoOpLspLogger.Instance, handlerProvider.Object); + executionQueue.Start(); + + return executionQueue; + } + + private static ILspServices GetLspServices() + { + var requestContextFactory = new Mock>(MockBehavior.Strict); + requestContextFactory.Setup(f => f.CreateRequestContextAsync(It.IsAny>(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new TestRequestContext())); + var services = new List<(Type, object)> { (typeof(IRequestContextFactory), requestContextFactory.Object) }; + var lspServices = new TestLspServices(services, supportsGetRegisteredServices: false); + + return lspServices; + } + + private static TestMethodHandler GetTestMethodHandler() + { + var methodHandler = new TestMethodHandler(); + + return methodHandler; + } + + [Fact] + public async Task ExecuteAsync_ThrowCompletes() + { + var throwingHandler = new ThrowingHandler(); + var requestExecutionQueue = GetRequestExecutionQueue(throwingHandler); + var request = 1; + var lspServices = GetLspServices(); + + await Assert.ThrowsAsync(() => requestExecutionQueue.ExecuteAsync(request, MethodName, lspServices, CancellationToken.None)); + } + + [Fact] + public async Task Dispose_MultipleTimes_Succeeds() + { + // Arrange + var requestExecutionQueue = GetRequestExecutionQueue(); + + // Act + await requestExecutionQueue.DisposeAsync(); + await requestExecutionQueue.DisposeAsync(); + + // Assert, it didn't fail + } + + public class ThrowingHandler : IRequestHandler + { + public bool MutatesSolutionState => false; + + public Task HandleRequestAsync(int request, TestRequestContext context, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } + + [Fact] + public async Task ExecuteAsync_CompletesTask() + { + var requestExecutionQueue = GetRequestExecutionQueue(); + var request = 1; + var lspServices = GetLspServices(); + + var response = await requestExecutionQueue.ExecuteAsync(request, MethodName, lspServices, CancellationToken.None); + + Assert.Equal("stuff", response); + } + + [Fact] + public async Task Queue_DrainsOnShutdown() + { + var requestExecutionQueue = GetRequestExecutionQueue(); + var request = 1; + var lspServices = GetLspServices(); + + var task1 = requestExecutionQueue.ExecuteAsync(request, MethodName, lspServices, CancellationToken.None); + var task2 = requestExecutionQueue.ExecuteAsync(request, MethodName, lspServices, CancellationToken.None); + + await requestExecutionQueue.DisposeAsync(); + + Assert.True(task1.IsCompleted); + Assert.True(task2.IsCompleted); + } + + private class TestResponse + { + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs new file mode 100644 index 0000000000000..6e668af2922dd --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs @@ -0,0 +1,142 @@ +// 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.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework.Example; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Nerdbank.Streams; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +internal class TestExampleLanguageServer : ExampleLanguageServer +{ + private readonly JsonRpc _clientRpc; + + public TestExampleLanguageServer(Stream clientSteam, JsonRpc jsonRpc, ILspLogger logger) : base(jsonRpc, logger) + { + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientSteam, clientSteam, CreateJsonMessageFormatter())) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; + + _clientRpc.Disconnected += _clientRpc_Disconnected; + + // This spins up the queue and ensure the LSP is ready to start receiving requests + Initialize(); + } + + public async Task ExecuteRequestAsync(string methodName, TRequest request, CancellationToken cancellationToken) + { + var result = await _clientRpc.InvokeWithParameterObjectAsync(methodName, request, cancellationToken); + + return result; + } + + internal async Task ExecuteNotificationAsync(string methodName, CancellationToken _) + { + await _clientRpc.NotifyAsync(methodName); + } + + protected override ILifeCycleManager GetLifeCycleManager() + { + return new TestLifeCycleManager(_shuttingDown, _exiting); + } + + private class TestLifeCycleManager : ILifeCycleManager + { + private readonly TaskCompletionSource _shuttingDownSource; + private readonly TaskCompletionSource _exitingSource; + + public TestLifeCycleManager(TaskCompletionSource shuttingDownSource, TaskCompletionSource exitingSource) + { + _shuttingDownSource = shuttingDownSource; + _exitingSource = exitingSource; + } + + public Task ExitAsync() + { + _exitingSource.SetResult(0); + return Task.CompletedTask; + } + + public Task ShutdownAsync(string message = "Shutting down") + { + _shuttingDownSource.SetResult(0); + return Task.CompletedTask; + } + } + + private readonly TaskCompletionSource _shuttingDown = new TaskCompletionSource(); + private readonly TaskCompletionSource _exiting = new TaskCompletionSource(); + + protected override ILspServices ConstructLspServices() + { + return base.ConstructLspServices(); + } + + private void _clientRpc_Disconnected(object sender, JsonRpcDisconnectedEventArgs e) + { + throw new NotImplementedException(); + } + + public void InitializeTest() + { + _clientRpc.StartListening(); + } + + public async Task WaitForShutdown() + { + return await _shuttingDown.Task; + } + + internal async Task WaitForExit() + { + return await _exiting.Task; + } + + private static JsonMessageFormatter CreateJsonMessageFormatter() + { + var messageFormatter = new JsonMessageFormatter(); + messageFormatter.JsonSerializer.AddVSInternalExtensionConverters(); + return messageFormatter; + } + + internal static TestExampleLanguageServer CreateLanguageServer(ILspLogger logger) + { + var (clientStream, serverStream) = FullDuplexStream.CreatePair(); + + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(serverStream, serverStream, CreateJsonMessageFormatter())); + + var server = new TestExampleLanguageServer(clientStream, jsonRpc, logger); + + jsonRpc.StartListening(); + server.InitializeTest(); + return server; + } + + internal async Task ShutdownServerAsync() + { + await ExecuteNotificationAsync(Methods.ShutdownName, CancellationToken.None); + } + + internal async Task InitializeServerAsync() + { + var request = new InitializeParams + { + Capabilities = new ClientCapabilities + { + + }, + }; + + var result = await ExecuteRequestAsync(Methods.InitializeName, request, CancellationToken.None); + + return result; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestLspServices.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestLspServices.cs new file mode 100644 index 0000000000000..631b2ce3376c0 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestLspServices.cs @@ -0,0 +1,66 @@ +// 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.Linq; +using Microsoft.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +internal class TestLspServices : ILspServices +{ + private readonly bool _supportsGetRegisteredServices; + private readonly IEnumerable<(Type, object)> _services; + + public TestLspServices(IEnumerable<(Type, object)> services, bool supportsGetRegisteredServices) + { + _services = services; + _supportsGetRegisteredServices = supportsGetRegisteredServices; + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public ImmutableArray GetRegisteredServices() + { + var types = new List(); + foreach (var service in _services) + { + types.Add(service.Item2.GetType()); + } + + return types.ToImmutableArray(); + } + + public T GetRequiredService() where T : notnull + { + var service = (T?)TryGetService(typeof(T)); + if (service is null) + throw new InvalidOperationException($"{typeof(T).Name} did not have a service"); + + return service; + } + + public IEnumerable GetRequiredServices() + { + var services = _services.Where(s => !_supportsGetRegisteredServices && s.Item2 is IMethodHandler).Select(s => (T)s.Item2); + return services; + } + + public bool SupportsGetRegisteredServices() + { + return _supportsGetRegisteredServices; + } + + public object? TryGetService(Type type) + { + var service = _services.FirstOrDefault(s => (_supportsGetRegisteredServices ? s.Item2.GetType() : s.Item1) == type); + + return service.Item2; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestRequestContext.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestRequestContext.cs new file mode 100644 index 0000000000000..7fa0b3383df60 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestRequestContext.cs @@ -0,0 +1,9 @@ +// 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. + +namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; + +public class TestRequestContext +{ +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs new file mode 100644 index 0000000000000..a80b2802dca2c --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs @@ -0,0 +1,376 @@ +// 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.Diagnostics.Contracts; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public abstract class AbstractLanguageServer +{ + private readonly JsonRpc _jsonRpc; + protected readonly ILspLogger _logger; + + /// + /// These are lazy to allow implementations to define custom variables that are used by + /// or + /// + private readonly Lazy> _queue; + private readonly Lazy _lspServices; + + public bool IsInitialized { get; private set; } + + /// + /// Ensures that we only run shutdown and exit code once in order. + /// Guards access to and + /// + private readonly object _lifeCycleLock = new(); + + /// + /// Task representing the work done on LSP server shutdown. + /// + private Task? _shutdownRequestTask; + + /// + /// Task representing the work down on LSP exit. + /// + private Task? _exitNotificationTask; + + /// + /// Task completion source that is started when the server starts and completes when the server exits. + /// Used when callers need to wait for the server to cleanup. + /// + private readonly TaskCompletionSource _serverExitedSource = new(); + + protected AbstractLanguageServer( + JsonRpc jsonRpc, + ILspLogger logger) + { + _logger = logger; + + _jsonRpc = jsonRpc; + _jsonRpc.AddLocalRpcTarget(this); + _jsonRpc.Disconnected += JsonRpc_Disconnected; + _lspServices = new Lazy(() => ConstructLspServices()); + _queue = new Lazy>(() => ConstructRequestExecutionQueue()); + } + + /// + /// Initializes the LanguageServer. + /// + /// Should be called at the bottom of the implementing constructor or immedietly after construction. + public void Initialize() + { + GetRequestExecutionQueue(); + } + + /// + /// Extension point to allow creation of since that can't always be handled in the constructor. + /// + /// An instance for this server. + /// This should only be called once, and then cached. + protected abstract ILspServices ConstructLspServices(); + + protected virtual IHandlerProvider GetHandlerProvider() + { + var lspServices = _lspServices.Value; + var handlerProvider = new HandlerProvider(lspServices); + SetupRequestDispatcher(handlerProvider); + + return handlerProvider; + } + + public ILspServices GetLspServices() => _lspServices.Value; + + protected virtual void SetupRequestDispatcher(IHandlerProvider handlerProvider) + { + var entryPointMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.EntryPointAsync)); + if (entryPointMethod is null) + throw new InvalidOperationException($"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.EntryPointAsync)}"); + var notificationMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.NotificationEntryPointAsync)); + if (notificationMethod is null) + throw new InvalidOperationException($"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.NotificationEntryPointAsync)}"); + + var parameterlessNotificationMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.ParameterlessNotificationEntryPointAsync)); + if (parameterlessNotificationMethod is null) + throw new InvalidOperationException($"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.ParameterlessNotificationEntryPointAsync)}"); + + foreach (var metadata in handlerProvider.GetRegisteredMethods()) + { + // Instead of concretely defining methods for each LSP method, we instead dynamically construct the + // generic method info from the exported handler types. This allows us to define multiple handlers for + // the same method but different type parameters. This is a key functionality to support TS external + // access as we do not want to couple our LSP protocol version dll to theirs. + // + // We also do not use the StreamJsonRpc support for JToken as the rpc method parameters because we want + // StreamJsonRpc to do the deserialization to handle streaming requests using IProgress. + var delegatingEntryPoint = new DelegatingEntryPoint(metadata.MethodName, this); + + MethodInfo genericEntryPointMethod; + if (metadata.RequestType is not null && metadata.ResponseType is not null) + { + genericEntryPointMethod = entryPointMethod.MakeGenericMethod(metadata.RequestType, metadata.ResponseType); + } + else if (metadata.RequestType is not null && metadata.ResponseType is null) + { + genericEntryPointMethod = notificationMethod.MakeGenericMethod(metadata.RequestType); + } + else if (metadata.RequestType is null && metadata.ResponseType is null) + { + // No need to genericize + genericEntryPointMethod = parameterlessNotificationMethod; + } + else + { + throw new NotImplementedException($"An unrecognized {nameof(RequestHandlerMetadata)} situation has occured"); + } + var methodAttribute = new JsonRpcMethodAttribute(metadata.MethodName) + { + UseSingleObjectParameterDeserialization = true, + }; + _jsonRpc.AddLocalRpcMethod(genericEntryPointMethod, delegatingEntryPoint, methodAttribute); + } + } + + [JsonRpcMethod("shutdown")] + public Task HandleShutdownRequestAsync(CancellationToken _) => ShutdownAsync(); + + [JsonRpcMethod("exit")] + public Task HandleExitNotificationAsync(CancellationToken _) => ExitAsync(); + + public virtual void OnInitialized() + { + IsInitialized = true; + } + + protected virtual IRequestExecutionQueue ConstructRequestExecutionQueue() + { + var handlerProvider = GetHandlerProvider(); + var queue = new RequestExecutionQueue(this, _logger, handlerProvider); + + queue.Start(); + + return queue; + } + + protected IRequestExecutionQueue GetRequestExecutionQueue() + { + return _queue.Value; + } + + /// + /// Wrapper class to hold the method and properties from the + /// that the method info passed to streamjsonrpc is created from. + /// + private class DelegatingEntryPoint + { + private readonly string _method; + private readonly AbstractLanguageServer _target; + + public DelegatingEntryPoint(string method, AbstractLanguageServer target) + { + _method = method; + _target = target; + } + + public async Task NotificationEntryPointAsync(TRequest request, CancellationToken cancellationToken) where TRequest : class + { + CheckServerState(); + var queue = _target.GetRequestExecutionQueue(); + var lspServices = _target.GetLspServices(); + + _ = await queue.ExecuteAsync(request, _method, lspServices, cancellationToken).ConfigureAwait(false); + } + + public async Task ParameterlessNotificationEntryPointAsync(CancellationToken cancellationToken) + { + CheckServerState(); + var queue = _target.GetRequestExecutionQueue(); + var lspServices = _target.GetLspServices(); + + _ = await queue.ExecuteAsync(VoidReturn.Instance, _method, lspServices, cancellationToken).ConfigureAwait(false); + } + + public async Task EntryPointAsync(TRequest request, CancellationToken cancellationToken) where TRequest : class + { + CheckServerState(); + var queue = _target.GetRequestExecutionQueue(); + var lspServices = _target.GetLspServices(); + + var result = await queue.ExecuteAsync(request, _method, lspServices, cancellationToken).ConfigureAwait(false); + + return result; + } + + private void CheckServerState() + { + if (_target.IsInitialized) + throw new InvalidOperationException($"'initialize' has not been called."); + } + } + + public Task WaitForExitAsync() + { + lock (_lifeCycleLock) + { + // Ensure we've actually been asked to shutdown before waiting. + if (_shutdownRequestTask == null) + { + throw new InvalidOperationException("The language server has not yet been asked to shutdown."); + } + } + + // Note - we return the _serverExitedSource task here instead of the _exitNotification task as we may not have + // finished processing the exit notification before a client calls into us asking to restart. + // This is because unlike shutdown, exit is a notification where clients do not need to wait for a response. + return _serverExitedSource.Task; + } + + /// + /// Tells the LSP server to stop handling any more incoming messages (other than exit). + /// Typically called from an LSP shutdown request. + /// + public Task ShutdownAsync(string message = "Shutting down") + { + Task shutdownTask; + lock (_lifeCycleLock) + { + // Run shutdown or return the already running shutdown request. + _shutdownRequestTask ??= Shutdown_NoLockAsync(message); + shutdownTask = _shutdownRequestTask; + return shutdownTask; + } + + // Runs the actual shutdown outside of the lock - guaranteed to be only called once by the above code. + async Task Shutdown_NoLockAsync(string message) + { + // Immediately yield so that this does not run under the lock. + await Task.Yield(); + + _logger.LogInformation(message); + + // Allow implementations to do any additional cleanup on shutdown. + var lifeCycleManager = GetLspServices().GetRequiredService(); + await lifeCycleManager.ShutdownAsync(message).ConfigureAwait(false); + + await ShutdownRequestExecutionQueueAsync().ConfigureAwait(false); + } + } + + /// + /// Tells the LSP server to exit. Requires that was called first. + /// Typically called from an LSP exit notification. + /// + public Task ExitAsync() + { + Task exitTask; + lock (_lifeCycleLock) + { + if (_shutdownRequestTask?.IsCompleted != true) + { + throw new InvalidOperationException("The language server has not yet been asked to shutdown or has not finished shutting down."); + } + + // Run exit or return the already running exit request. + _exitNotificationTask ??= Exit_NoLockAsync(); + exitTask = _exitNotificationTask; + return exitTask; + } + + // Runs the actual exit outside of the lock - guaranteed to be only called once by the above code. + async Task Exit_NoLockAsync() + { + // Immediately yield so that this does not run under the lock. + await Task.Yield(); + + try + { + var lspServices = GetLspServices(); + + // Allow implementations to do any additional cleanup on exit. + var lifeCycleManager = lspServices.GetRequiredService(); + await lifeCycleManager.ExitAsync().ConfigureAwait(false); + + await ShutdownRequestExecutionQueueAsync().ConfigureAwait(false); + + lspServices.Dispose(); + + _jsonRpc.Disconnected -= JsonRpc_Disconnected; + _jsonRpc.Dispose(); + } + catch (Exception) + { + // Swallow exceptions thrown by disposing our JsonRpc object. Disconnected events can potentially throw their own exceptions so + // we purposefully ignore all of those exceptions in an effort to shutdown gracefully. + } + finally + { + _logger.LogInformation("Exiting server"); + _serverExitedSource.TrySetResult(null); + } + } + } + + private ValueTask ShutdownRequestExecutionQueueAsync() + { + var queue = GetRequestExecutionQueue(); + return queue.DisposeAsync(); + } + +#pragma warning disable VSTHRD100 + /// + /// Cleanup the server if we encounter a json rpc disconnect so that we can be restarted later. + /// + private async void JsonRpc_Disconnected(object? sender, JsonRpcDisconnectedEventArgs e) + { + // It is possible this gets called during normal shutdown and exit. + // ShutdownAsync and ExitAsync will no-op if shutdown was already triggered by something else. + await ShutdownAsync(message: "Shutdown triggered by JsonRpc disconnect").ConfigureAwait(false); + await ExitAsync().ConfigureAwait(false); + } +#pragma warning disable VSTHRD100 + + internal TestAccessor GetTestAccessor() + { + return new(this); + } + + internal readonly struct TestAccessor + { + private readonly AbstractLanguageServer _server; + + internal TestAccessor(AbstractLanguageServer server) + { + _server = server; + } + + public T GetRequiredLspService() where T : class => _server.GetLspServices().GetRequiredService(); + + internal RequestExecutionQueue.TestAccessor? GetQueueAccessor() + { + if (_server._queue.Value is RequestExecutionQueue requestExecution) + return requestExecution.GetTestAccessor(); + + return null; + } + + internal JsonRpc GetServerRpc() => _server._jsonRpc; + + internal bool HasShutdownStarted() + { + lock (_server._lifeCycleLock) + { + return _server._shutdownRequestTask != null; + } + } + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs new file mode 100644 index 0000000000000..7a89e5cfb9dc6 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs @@ -0,0 +1,225 @@ +// 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.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +internal class HandlerProvider : IHandlerProvider +{ + private readonly ILspServices _lspServices; + private ImmutableDictionary>? _requestHandlers; + + public HandlerProvider(ILspServices lspServices) + { + _lspServices = lspServices; + } + + /// + /// Get the MethodHandler for a particular request. + /// + /// The method name being made. + /// The requestType for this method. + /// The responseType for this method. + /// The handler for this request. + public IMethodHandler GetMethodHandler(string method, Type? requestType, Type? responseType) + { + var requestHandlerMetadata = new RequestHandlerMetadata(method, requestType, responseType); + + var requestHandlers = GetRequestHandlers(); + if (!requestHandlers.TryGetValue(requestHandlerMetadata, out var lazyHandler)) + { + throw new InvalidOperationException($"Missing handler for {requestHandlerMetadata.MethodName}"); + } + var handler = lazyHandler.Value; + + return handler; + } + + public ImmutableArray GetRegisteredMethods() + { + var requestHandlers = GetRequestHandlers(); + return requestHandlers.Keys.ToImmutableArray(); + } + + private ImmutableDictionary> GetRequestHandlers() + { + if (_requestHandlers is null) + { + _requestHandlers = CreateMethodToHandlerMap(_lspServices); + } + + return _requestHandlers; + } + + private static ImmutableDictionary> CreateMethodToHandlerMap(ILspServices lspServices) + { + var requestHandlerDictionary = ImmutableDictionary.CreateBuilder>(); + + if (lspServices.SupportsGetRegisteredServices()) + { + var requestHandlerTypes = lspServices.GetRegisteredServices().Where(type => IsTypeRequestHandler(type)); + + foreach (var handlerType in requestHandlerTypes) + { + var (requestType, responseType, requestContext) = ConvertHandlerTypeToRequestResponseTypes(handlerType); + var method = GetRequestHandlerMethod(handlerType); + + // Using the lazy set of handlers, create a lazy instance that will resolve the set of handlers for the provider + // and then lookup the correct handler for the specified method. + + CheckForDuplicates(method, handlerType, requestHandlerDictionary); + + requestHandlerDictionary.Add(new RequestHandlerMetadata(method, requestType, responseType), new Lazy(() => + { + var lspService = lspServices.TryGetService(handlerType); + if (lspService is null) + { + throw new InvalidOperationException($"{handlerType} could not be retrieved from service"); + } + + return (IMethodHandler)lspService; + })); + } + } + + var handlers = lspServices.GetRequiredServices(); + + foreach (var handler in handlers) + { + var handlerType = handler.GetType(); + var (requestType, responseType, requestContext) = ConvertHandlerTypeToRequestResponseTypes(handlerType); + var method = GetRequestHandlerMethod(handlerType); + CheckForDuplicates(method, handlerType, requestHandlerDictionary); + + requestHandlerDictionary.Add(new RequestHandlerMetadata(method, requestType, responseType), new Lazy(() => handler)); + } + + VerifyHandlers(requestHandlerDictionary.Keys); + + return requestHandlerDictionary.ToImmutable(); + + static void CheckForDuplicates(string methodName, Type handlerType, ImmutableDictionary>.Builder handlerDict) + { + var dupHandlers = handlerDict.Where(kvp => string.Equals(kvp.Key.MethodName, methodName, StringComparison.InvariantCulture)); + if (dupHandlers.Any()) + { + throw new InvalidOperationException($"Method {methodName} was implemented by both {handlerType} and {dupHandlers.First().Key}"); + } + } + + static string GetRequestHandlerMethod(Type handlerType) + { + // Get the LSP method name from the handler's method name attribute. + var methodAttribute = GetMethodAttribute(handlerType); + if (methodAttribute is null) + { + throw new InvalidOperationException($"{handlerType.FullName} is missing {nameof(LanguageServerEndpointAttribute)}"); + } + return methodAttribute.Method; + + static LanguageServerEndpointAttribute? GetMethodAttribute(Type type) + { + var attribute = Attribute.GetCustomAttribute(type, typeof(LanguageServerEndpointAttribute)) as LanguageServerEndpointAttribute; + if (attribute is null) + { + var interfaces = type.GetInterfaces(); + foreach (var @interface in interfaces) + { + attribute = GetMethodAttribute(@interface); + if (attribute is not null) + { + break; + } + } + } + + return attribute; + } + } + + static bool IsTypeRequestHandler(Type type) + { + return type.GetInterfaces().Contains(typeof(IMethodHandler)); + } + + static void VerifyHandlers(IEnumerable requestHandlerKeys) + { + var missingMethods = requestHandlerKeys.Where(meta => RequiredMethods.All(method => method == meta.MethodName)); + if (missingMethods.Count() > 0) + { + throw new InvalidOperationException($"Language Server is missing required methods {string.Join(",", missingMethods)}"); + } + } + } + + private static readonly IReadOnlyList RequiredMethods = new List { "initialize", "initialized", "shutdown", "exit" }; + + /// + /// Retrieves the generic argument information from the request handler type without instantiating it. + /// + private static (Type? requestType, Type? responseType, Type requestContext) ConvertHandlerTypeToRequestResponseTypes(Type handlerType) + { + var requestHandlerGenericType = handlerType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequestHandler<,,>)).SingleOrDefault(); + var parameterlessNotificationHandlerGenericType = handlerType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(INotificationHandler<>)).SingleOrDefault(); + var notificationHandlerGenericType = handlerType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(INotificationHandler<,>)).SingleOrDefault(); + + Type? requestType; + Type? responseType; + Type requestContext; + if (requestHandlerGenericType is not null) + { + var genericArguments = requestHandlerGenericType.GetGenericArguments(); + + if (genericArguments.Length != 3) + { + throw new InvalidOperationException($"Provided handler type {handlerType.FullName} does not have exactly three generic arguments"); + } + + requestType = genericArguments[0]; + responseType = genericArguments[1]; + requestContext = genericArguments[2]; + } + else if (parameterlessNotificationHandlerGenericType is not null) + { + var genericArguments = parameterlessNotificationHandlerGenericType.GetGenericArguments(); + + if (genericArguments.Length != 1) + { + throw new InvalidOperationException($"Provided handler type {handlerType.FullName} does not have exactly 1 generic argument"); + } + + requestType = null; + responseType = null; + requestContext = genericArguments[0]; + } + else if (notificationHandlerGenericType is not null) + { + var genericArguments = notificationHandlerGenericType.GetGenericArguments(); + + if (genericArguments.Length != 2) + { + throw new InvalidOperationException($"Provided handler type {handlerType.FullName} does not have exactly 2 generic arguments"); + } + + requestType = genericArguments[0]; + responseType = null; + requestContext = genericArguments[1]; + } + else + { + throw new InvalidOperationException($"Provided handler type {handlerType.FullName} does not implement {typeof(IRequestHandler<,,>).Name}, {typeof(INotificationHandler<>).Name} or {typeof(INotificationHandler<,>).Name}"); + } + + return (requestType, responseType, requestContext); + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializeHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializeHandler.cs new file mode 100644 index 0000000000000..78e7568d2ae2a --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializeHandler.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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Handlers; + +[LanguageServerEndpoint("initialize")] +public class InitializeHandler + : IRequestHandler +{ + private readonly IInitializeManager _capabilitiesManager; + + public InitializeHandler(IInitializeManager capabilitiesManager) + { + _capabilitiesManager = capabilitiesManager; + } + + public bool MutatesSolutionState => true; + + public bool RequiresLSPSolution => false; + + public Task HandleRequestAsync(TRequest request, TRequestContext context, CancellationToken cancellationToken) + { + _capabilitiesManager.SetInitializeParams(request); + + var serverCapabilities = _capabilitiesManager.GetInitializeResult(); + + return Task.FromResult(serverCapabilities); + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializedHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializedHandler.cs new file mode 100644 index 0000000000000..3236d5c8d164c --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Handlers/InitializedHandler.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 System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework.Handlers; + +[LanguageServerEndpoint("initialized")] +public class InitializedHandler : INotificationHandler +{ + private bool HasBeenInitialized = false; + + public bool MutatesSolutionState => true; + + public bool RequiresLSPSolution => true; + + public Task HandleNotificationAsync(TRequest request, TRequestContext requestContext, CancellationToken cancellationToken) + { + if (HasBeenInitialized) + { + throw new InvalidOperationException("initialized was called twice"); + } + + HasBeenInitialized = true; + + return Task.CompletedTask; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IHandlerProvider.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IHandlerProvider.cs new file mode 100644 index 0000000000000..5c8a6ae60d04a --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IHandlerProvider.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; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Manages handler discovery and distribution. +/// +public interface IHandlerProvider +{ + ImmutableArray GetRegisteredMethods(); + + IMethodHandler GetMethodHandler(string method, Type? requestType, Type? responseType); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IInitializeManager.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IInitializeManager.cs new file mode 100644 index 0000000000000..2bddb76a55ef5 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IInitializeManager.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. + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public interface IInitializeManager +{ + /// + /// Gets a response to be used for "initialize", completing the negoticaitons between client and server. + /// + /// An InitializeResult. + TResponse GetInitializeResult(); + + /// + /// Store the InitializeParams for later retrieval. + /// + /// The InitializeParams to be stored. + void SetInitializeParams(TRequest request); + + /// + /// Gets the InitializeParams to, for example, examine the ClientCapabilities. + /// + /// The InitializeParams object sent with "initialize". + TRequest GetInitializeParams(); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILifeCycleManager.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILifeCycleManager.cs new file mode 100644 index 0000000000000..4f657579a7e7a --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILifeCycleManager.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.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// An optional component to run additional logic when LSP shutdown and exit are called, +/// for example logging messages, cleaning up custom resources, etc. +/// +public interface ILifeCycleManager +{ + /// + /// Called when the server recieves the LSP exit notification. + /// + /// + /// This is always called after the LSP shutdown request and runs + /// but before LSP services and the JsonRpc connection is disposed of in LSP exit. + /// Implementations are not expected to be threadsafe. + /// + Task ExitAsync(); + + /// + /// Called when the server receives the LSP shutdown request. + /// + /// + /// This is called before the request execution is closed. + /// Implementations are not expected to be threadsafe. + /// + Task ShutdownAsync(string message = "Shutting down"); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspLogger.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspLogger.cs new file mode 100644 index 0000000000000..69c1c91255734 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspLogger.cs @@ -0,0 +1,19 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public interface ILspLogger +{ + void LogStartContext(string message, params object[] @params); + void LogEndContext(string message, params object[] @params); + void LogInformation(string message, params object[] @params); + void LogWarning(string message, params object[] @params); + void LogError(string message, params object[] @params); + void LogException(Exception exception, string? message = null, params object[] @params); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspServices.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspServices.cs new file mode 100644 index 0000000000000..99febdc229d96 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ILspServices.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; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public interface ILspServices : IDisposable +{ + T GetRequiredService() where T : notnull; + + object? TryGetService(Type @type); + + IEnumerable GetRequiredServices(); + + // TODO: https://github.com/dotnet/roslyn/issues/63555 + // These two methods should ideally be removed, but that would required + // Roslyn to allow non-lazy creation of IMethodHandlers which they currently cannot + ImmutableArray GetRegisteredServices(); + + bool SupportsGetRegisteredServices(); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IMethodHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IMethodHandler.cs new file mode 100644 index 0000000000000..d63092bfcb064 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IMethodHandler.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Top level type for LSP request handler. +/// +public interface IMethodHandler +{ + /// + /// Whether or not the solution state on the server is modified as a part of handling this request. + /// This may affect queuing behavior (IE mutating requests are run in serial rather than paralel) depending on the implementation. + /// + bool MutatesSolutionState { get; } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/INotificationHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/INotificationHandler.cs new file mode 100644 index 0000000000000..ab20dc4500c44 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/INotificationHandler.cs @@ -0,0 +1,27 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// An interface for handlers of methods which do not return a response and receive no parameters. +/// +/// The type of the RequestContext to be used by this handler. +public interface INotificationHandler : IMethodHandler +{ + Task HandleNotificationAsync(TRequestContext requestContext, CancellationToken cancellationToken); +} + +/// +/// An interface for handlers of methods which do not return a response +/// +/// The type of the Request parameter to be received. +/// The type of the RequestContext to be used by this handler. +public interface INotificationHandler : IMethodHandler +{ + Task HandleNotificationAsync(TRequest request, TRequestContext requestContext, CancellationToken cancellationToken); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IQueueItem.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IQueueItem.cs new file mode 100644 index 0000000000000..95236f4417770 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IQueueItem.cs @@ -0,0 +1,49 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// An item to be queued for execution. +/// +/// The type of the request context to be passed along to the handler. +public interface IQueueItem +{ + /// + /// Executes the work specified by this queue item. + /// + /// the context created by + /// + /// A which completes when the request has finished. + Task StartRequestAsync(TRequestContext? requestContext, CancellationToken cancellationToken); + + /// + /// Creates the context that is sent to the handler for this queue item. + /// Note - this method is always called serially inside the queue before + /// running the actual request in + /// Throwing in this method will cause the server to shutdown. + /// + Task CreateRequestContextAsync(CancellationToken cancellationToken); + + ILspServices LspServices { get; } + + /// + /// Indicates that this request may mutate the server state, so that the queue may handle its execution appropriatly. + /// + bool MutatesServerState { get; } + + /// + /// The method being executed. + /// + string MethodName { get; } + + /// + /// The handler which will run this operation. + /// + IMethodHandler MethodHandler { get; } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestContextFactory.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestContextFactory.cs new file mode 100644 index 0000000000000..71bb0e5100397 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestContextFactory.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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// +/// A factory for creating objects from 's. +/// +/// +/// RequestContext's are useful for passing document context, since by default +/// is run on the queue thread (and thus no mutating requests may be executing simultaniously, preventing possible race conditions). +/// It also allows somewhere to pass things like the or which are useful on a wide variety of requests. +/// +/// +/// The type of the RequestContext to be used by the handler. +public interface IRequestContextFactory +{ + /// + /// Create a object from the given . + /// Note - throwing in the implementation of this method will cause the server to shutdown. + /// + /// The from which to create a request. + /// The request parameters. + /// + /// The for this request. + /// This method is called on the queue thread to allow context to be retrieved serially, without the posibility of race conditions from Mutating requests. + Task CreateRequestContextAsync(IQueueItem queueItem, TRequestParam requestParam, CancellationToken cancellationToken); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestExecutionQueue.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestExecutionQueue.cs new file mode 100644 index 0000000000000..5f36c4e8477b3 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestExecutionQueue.cs @@ -0,0 +1,27 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Queues requests to be executed in the proper order. +/// +/// The type of the RequestContext to be used by the handler. +public interface IRequestExecutionQueue : IAsyncDisposable +{ + /// + /// Queue a request. + /// + /// A task that completes when the handler execution is done. + Task ExecuteAsync(TRequest request, string methodName, ILspServices lspServices, CancellationToken cancellationToken); + + /// + /// Start the queue accepting requests once any event handlers have been attached. + /// + void Start(); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs new file mode 100644 index 0000000000000..26c68595bca2f --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs @@ -0,0 +1,22 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public interface IRequestHandler : IMethodHandler +{ + /// + /// Handles an LSP request in the context of the supplied document and/or solutuion. + /// + /// The request parameters. + /// The LSP request context, which should have been filled in with document information from if applicable. + /// A cancellation token that can be used to cancel the request processing. + /// The LSP response. + Task HandleRequestAsync(TRequest request, TRequestContext context, CancellationToken cancellationToken); +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ITextDocumentIdentifierHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ITextDocumentIdentifierHandler.cs new file mode 100644 index 0000000000000..542bb422576a4 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/ITextDocumentIdentifierHandler.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public interface ITextDocumentIdentifierHandler : ITextDocumentIdentifierHandler +{ + /// + /// Gets the identifier of the document from the request, if the request provides one. + /// + TTextDocumentIdentifier GetTextDocumentIdentifier(TRequest request); +} + +public interface ITextDocumentIdentifierHandler +{ +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IsExternalInit.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IsExternalInit.cs new file mode 100644 index 0000000000000..a78db8a9f3854 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IsExternalInit.cs @@ -0,0 +1,13 @@ +// 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; + +namespace System.Runtime.CompilerServices +{ + public class IsExternalInit { } +} + diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs new file mode 100644 index 0000000000000..1c504f8c51bb2 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs @@ -0,0 +1,24 @@ +// 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; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// An attribute which identifies the method which an implements. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] +public class LanguageServerEndpointAttribute : Attribute +{ + /// + /// Contains the method that this implements. + /// + public string Method { get; } + + public LanguageServerEndpointAttribute(string method) + { + Method = method; + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/License.txt b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/License.txt new file mode 100644 index 0000000000000..9a51ac920f21a --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/License.txt @@ -0,0 +1,37 @@ +MICROSOFT SOFTWARE LICENSE TERMS +MICROSOFT VISUAL STUDIO ADD-ONs and EXTENSIONS +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to use solely with +Visual Studio Community +Visual Studio Professional +Visual Studio Enterprise +Visual Studio Code +2. THIRD PARTY COMPONENTS. +The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. +3. DATA. +a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications and you should provide a copy of Microsoft's privacy statement to your users. The Microsoft privacy statement is located here https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. +b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. +4. SCOPE OF LICENSE. The software is licensed, not sold. These license terms only give you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in these license terms. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. In addition, you may not +* work around any technical limitations in the software; +* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software; +* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +* use the software in any way that is against the law; +* share, publish, rent, or lease the software; or +* provide the software as a stand-alone offering or combine it with any of your applications for others to use, or transfer the software or this agreement to any third party. +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. +6. SUPPORT SERVICES. Because this software is "as is", we may not provide support services for it. +7. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. These license terms describe certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. You may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. You may stop receiving updates on your device by turning off Internet access. If and when you re-connect to the Internet, the software will resume checking for and installing updates. +c. Germany and Austria. +(i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. +(ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in the case of death or personal or physical injury, Microsoft is liable according to the statutory law. +Subject to the preceding sentence (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED "AS-IS". YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +11. LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages. diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.csproj b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.csproj new file mode 100644 index 0000000000000..79ae25f964c98 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.csproj @@ -0,0 +1,28 @@ + + + + + Library + netstandard2.0 + true + + A framework for building Language Server Protocol implementations in C#. + + + License.txt + + + + + + + + + + + + + + + + diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs new file mode 100644 index 0000000000000..1a7049af95c7e --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs @@ -0,0 +1,174 @@ +// 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.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// A placeholder type to help handle Notification messages. +/// +internal record VoidReturn +{ + public static VoidReturn Instance = new(); +} + +internal class QueueItem : IQueueItem +{ + private readonly ILspLogger _logger; + + private readonly TRequest _request; + private readonly IMethodHandler _handler; + + /// + /// A task completion source representing the result of this queue item's work. + /// This is the task that the client is waiting on. + /// + private readonly TaskCompletionSource _completionSource = new(); + + public ILspServices LspServices { get; } + + public bool MutatesServerState { get; } + + public string MethodName { get; } + + public IMethodHandler MethodHandler { get; } + + private QueueItem( + bool mutatesSolutionState, + string methodName, + IMethodHandler methodHandler, + TRequest request, + IMethodHandler handler, + ILspServices lspServices, + ILspLogger logger, + CancellationToken cancellationToken) + { + // Set the tcs state to cancelled if the token gets cancelled outside of our callback (for example the server shutting down). + cancellationToken.Register(() => _completionSource.TrySetCanceled(cancellationToken)); + + _handler = handler; + _logger = logger; + _request = request; + LspServices = lspServices; + MethodHandler = methodHandler; + + MutatesServerState = mutatesSolutionState; + MethodName = methodName; + } + + public static (IQueueItem, Task) Create( + bool mutatesSolutionState, + string methodName, + IMethodHandler methodHandler, + TRequest request, + IMethodHandler handler, + ILspServices lspServices, + ILspLogger logger, + CancellationToken cancellationToken) + { + var queueItem = new QueueItem( + mutatesSolutionState, + methodName, + methodHandler, + request, + handler, + lspServices, + logger, + cancellationToken); + + return (queueItem, queueItem._completionSource.Task); + } + + public async Task CreateRequestContextAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var requestContextFactory = LspServices.GetRequiredService>(); + var context = await requestContextFactory.CreateRequestContextAsync(this, _request, cancellationToken).ConfigureAwait(false); + return context; + } + + /// + /// Processes the queued request. Exceptions will be sent to the task completion source + /// representing the task that the client is waiting for, then re-thrown so that + /// the queue can correctly handle them depending on the type of request. + /// + /// + /// The result of the request. + public async Task StartRequestAsync(TRequestContext? context, CancellationToken cancellationToken) + { + _logger.LogStartContext($"{MethodName}"); + try + { + cancellationToken.ThrowIfCancellationRequested(); + + if (context is null) + { + // If we weren't able to get a corresponding context for this request (for example, we + // couldn't map a doc request to a particular Document, or we couldn't find an appropriate + // Workspace for a global operation), then just immediately complete the request with a + // 'null' response. Note: the lsp spec was checked to ensure that 'null' is valid for all + // the requests this could happen for. However, this assumption may not hold in the future. + // If that turns out to be the case, we could defer to the individual handler to decide + // what to do. + _logger.LogWarning($"Could not get request context for {MethodName}"); + _completionSource.TrySetException(new InvalidOperationException($"Unable to create request context for {MethodName}")); + } + else + { + if (_handler is IRequestHandler requestHandler) + { + var result = await requestHandler.HandleRequestAsync(_request, context, cancellationToken).ConfigureAwait(false); + + _completionSource.TrySetResult(result); + } + else if (_handler is INotificationHandler notificationHandler) + { + await notificationHandler.HandleNotificationAsync(_request, context, cancellationToken).ConfigureAwait(false); + + // We know that the return type of will always be even if the compiler doesn't. + _completionSource.TrySetResult((TResponse)(object)VoidReturn.Instance); + } + else if (_handler is INotificationHandler parameterlessNotificationHandler) + { + await parameterlessNotificationHandler.HandleNotificationAsync(context, cancellationToken).ConfigureAwait(false); + + // We know that the return type of will always be even if the compiler doesn't. + _completionSource.TrySetResult((TResponse)(object)VoidReturn.Instance); + } + else + { + throw new NotImplementedException($"Unrecognized {nameof(IMethodHandler)} implementation {_handler.GetType().Name}"); + } + } + } + catch (OperationCanceledException ex) + { + // Record logs + metrics on cancellation. + _logger.LogInformation($"{MethodName} - Canceled"); + + _completionSource.TrySetCanceled(ex.CancellationToken); + } + catch (Exception ex) + { + // Record logs and metrics on the exception. + // It's important that this can NEVER throw, or the queue will hang. + _logger.LogException(ex); + + _completionSource.TrySetException(ex); + } + finally + { + _logger.LogEndContext($"{MethodName}"); + } + + // Return the result of this completion source to the caller + // so it can decide how to handle the result / exception. + await _completionSource.Task.ConfigureAwait(false); + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs new file mode 100644 index 0000000000000..1e2b21efc9a7a --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -0,0 +1,295 @@ +// 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.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; +using System.Collections.Immutable; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Coordinates the exectution of LSP messages to ensure correct results are sent back. +/// +/// +/// +/// When a request comes in for some data the handler must be able to access a solution state that is correct +/// at the time of the request, that takes into account any text change requests that have come in previously +/// (via textDocument/didChange for example). +/// +/// +/// This class acheives this by distinguishing between mutating and non-mutating requests, and ensuring that +/// when a mutating request comes in, its processing blocks all subsequent requests. As each request comes in +/// it is added to a queue, and a queue item will not be retrieved while a mutating request is running. Before +/// any request is handled the solution state is created by merging workspace solution state, which could have +/// changes from non-LSP means (eg, adding a project reference), with the current "mutated" state. +/// When a non-mutating work item is retrieved from the queue, it is given the current solution state, but then +/// run in a fire-and-forget fashion. +/// +/// +/// Regardless of whether a request is mutating or not, or blocking or not, is an implementation detail of this class +/// and any consumers observing the results of the task returned from +/// +/// will see the results of the handling of the request, whenever it occurred. +/// +/// +/// Exceptions in the handling of non-mutating requests are sent back to callers. Exceptions in the processing of +/// the queue will close the LSP connection so that the client can reconnect. Exceptions in the handling of mutating +/// requests will also close the LSP connection, as at that point the mutated solution is in an unknown state. +/// +/// +/// After shutdown is called, or an error causes the closing of the connection, the queue will not accept any +/// more messages, and a new queue will need to be created. +/// +/// +public class RequestExecutionQueue : IRequestExecutionQueue +{ + protected readonly ILspLogger _logger; + private readonly IHandlerProvider _handlerProvider; + private readonly AbstractLanguageServer _languageServer; + + /// + /// The queue containing the ordered LSP requests along with the trace activityId (to associate logs with a request) and + /// a combined cancellation token representing the queue's cancellation token and the individual request cancellation token. + /// + protected readonly AsyncQueue<(IQueueItem queueItem, Guid ActivityId, CancellationToken cancellationToken)> _queue = new(); + private readonly CancellationTokenSource _cancelSource = new(); + + /// + /// For test purposes only. + /// A task that completes when the queue processing stops. + /// + protected Task? _queueProcessingTask; + + public CancellationToken CancellationToken => _cancelSource.Token; + + public RequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, IHandlerProvider handlerProvider) + { + _languageServer = languageServer; + _logger = logger; + _handlerProvider = handlerProvider; + } + + public void Start() + { + // Start the queue processing + _queueProcessingTask = ProcessQueueAsync(); + } + + protected IMethodHandler GetMethodHandler(string methodName) + { + var requestType = typeof(TRequest) == typeof(VoidReturn) ? null : typeof(TRequest); + var responseType = typeof(TResponse) == typeof(VoidReturn) ? null : typeof(TResponse); + + var handler = _handlerProvider.GetMethodHandler(methodName, requestType, responseType); + + return handler; + } + + /// + /// Queues a request to be handled by the specified handler, with mutating requests blocking subsequent requests + /// from starting until the mutation is complete. + /// + /// The request to handle. + /// The name of the LSP method. + /// A cancellation token that will cancel the handing of this request. + /// The request could also be cancelled by the queue shutting down. + /// A task that can be awaited to observe the results of the handing of this request. + public virtual Task ExecuteAsync( + TRequest request, + string methodName, + ILspServices lspServices, + CancellationToken requestCancellationToken) + { + // Note: If the queue is not accepting any more items then TryEnqueue below will fail. + + var handler = GetMethodHandler(methodName); + // Create a combined cancellation token so either the client cancelling it's token or the queue + // shutting down cancels the request. + var combinedTokenSource = _cancelSource.Token.CombineWith(requestCancellationToken); + var combinedCancellationToken = combinedTokenSource.Token; + var (item, resultTask) = CreateQueueItem( + handler.MutatesSolutionState, + methodName, + handler, + request, + handler, + lspServices, + combinedCancellationToken); + + // Run a continuation to ensure the cts is disposed of. + // We pass CancellationToken.None as we always want to dispose of the source + // even when the request is cancelled or the queue is shutting down. + _ = resultTask.ContinueWith(_ => combinedTokenSource.Dispose(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + var didEnqueue = _queue.TryEnqueue((item, Trace.CorrelationManager.ActivityId, combinedCancellationToken)); + + // If the queue has been shut down the enqueue will fail, so we just fault the task immediately. + // The queue itself is threadsafe (_queue.TryEnqueue and _queue.Complete use the same lock). + if (!didEnqueue) + return Task.FromException(new InvalidOperationException("Server was requested to shut down.")); + + return resultTask; + } + + internal (IQueueItem, Task) CreateQueueItem( + bool mutatesSolutionState, + string methodName, + IMethodHandler methodHandler, + TRequest request, + IMethodHandler handler, + ILspServices lspServices, + CancellationToken cancellationToken) => QueueItem.Create(mutatesSolutionState, + methodName, + methodHandler, + request, + handler, + lspServices, + _logger, + cancellationToken); + + private async Task ProcessQueueAsync() + { + ILspServices? lspServices = null; + try + { + while (!_cancelSource.IsCancellationRequested) + { + // First attempt to de-queue the work item in its own try-catch. + // This is because before we de-queue we do not have access to the queue item's linked cancellation token. + (IQueueItem work, Guid activityId, CancellationToken cancellationToken) queueItem; + try + { + queueItem = await _queue.DequeueAsync(_cancelSource.Token).ConfigureAwait(false); + } + catch (OperationCanceledException ex) when (ex.CancellationToken == _cancelSource.Token) + { + // The queue's cancellation token was invoked which means we are shutting down the queue. + // Exit out of the loop so we stop processing new items. + return; + } + + try + { + var (work, activityId, cancellationToken) = queueItem; + lspServices = work.LspServices; + + var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken, cancellationToken); + + // Restore our activity id so that logging/tracking works across asynchronous calls. + Trace.CorrelationManager.ActivityId = activityId; + // The request context must be created serially inside the queue to so that requests always run + // on the correct snapshot as of the last request. + var context = await work.CreateRequestContextAsync(cancellationToken).ConfigureAwait(false); + if (work.MutatesServerState) + { + // Mutating requests block other requests from starting to ensure an up to date snapshot is used. + // Since we're explicitly awaiting exceptions to mutating requests will bubble up here. + await WrapStartRequestTaskAsync(work.StartRequestAsync(context, cancellationToken), rethrowExceptions: true).ConfigureAwait(false); + } + else + { + // Non mutating are fire-and-forget because they are by definition readonly. Any errors + // will be sent back to the client but they can also be captured via HandleNonMutatingRequestError, + // though these errors don't put us into a bad state as far as the rest of the queue goes. + // Furthermore we use Task.Run here to protect ourselves against synchronous execution of work + // blocking the request queue for longer periods of time (it enforces parallelizabilty). + _ = WrapStartRequestTaskAsync(Task.Run(() => work.StartRequestAsync(context, cancellationToken), cancellationToken), rethrowExceptions: false); + } + } + catch (OperationCanceledException ex) when (ex.CancellationToken == queueItem.cancellationToken) + { + // Explicitly ignore this exception as cancellation occured as a result of our linked cancellation token. + // This means either the queue is shutting down or the request itself was cancelled. + // 1. If the queue is shutting down, then while loop will exit before the next iteration since it checks for cancellation. + // 2. Request cancellations are normal so no need to report anything there. + } + } + } + catch (Exception ex) + { + // We encountered an unexpected exception in processing the queue or in a mutating request. + // Log it, shutdown the queue, and exit the loop. + _logger.LogException(ex); + var message = $"Error occurred processing queue: {ex.Message}."; + if (lspServices is not null) + { + await _languageServer.ShutdownAsync("Error processing queue, shutting down").ConfigureAwait(false); + await _languageServer.ExitAsync().ConfigureAwait(false); + } + + await DisposeAsync().ConfigureAwait(false); + return; + } + } + + /// + /// Provides an extensiblity point to log or otherwise inspect errors thrown from non-mutating requests, + /// which would otherwise be lost to the fire-and-forget task in the queue. + /// + /// The task to be inspected. + /// The task from , to allow chained calls if needed. + public virtual Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + { + return nonMutatingRequestTask; + } + + /// + /// Shuts down the queue, stops accepting new messages, and cancels any in-progress or queued tasks. + /// + public ValueTask DisposeAsync() + { + _cancelSource.Cancel(); + + // Tell the queue not to accept any more items. + // Note: We do not need to spin through the queue manually and cancel items as + // 1. New queue instances are created for each server, so items in the queue would be gc'd. + // 2. Their cancellation tokens are linked to the queue's _cancelSource so are also cancelled. + _queue.Complete(); + + return new ValueTask(); + } + + #region Test Accessor + internal TestAccessor GetTestAccessor() + => new(this); + + internal readonly struct TestAccessor + { + private readonly RequestExecutionQueue _queue; + + public TestAccessor(RequestExecutionQueue queue) + => _queue = queue; + + public bool IsComplete() => _queue._queue.IsCompleted && _queue._queue.IsEmpty; + + public async Task WaitForProcessingToStopAsync() + { + if (_queue._queueProcessingTask is not null) + { + await _queue._queueProcessingTask.ConfigureAwait(false); + } + } + + /// + /// Test only method to validate that remaining items in the queue are cancelled. + /// This directly mutates the queue in an unsafe way, so ensure that all relevant queue operations + /// are done before calling. + /// + public async Task AreAllItemsCancelledUnsafeAsync() + { + while (!_queue._queue.IsEmpty) + { + var (_, _, cancellationToken) = await _queue._queue.DequeueAsync().ConfigureAwait(false); + if (!cancellationToken.IsCancellationRequested) + return false; + } + + return true; + } + } + #endregion +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestHandlerMetadata.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestHandlerMetadata.cs new file mode 100644 index 0000000000000..5f4136a07cd8d --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestHandlerMetadata.cs @@ -0,0 +1,9 @@ +// 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; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public record RequestHandlerMetadata(string MethodName, Type? RequestType, Type? ResponseType); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestShutdownEventArgs.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestShutdownEventArgs.cs new file mode 100644 index 0000000000000..ce7a816dfe9f4 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestShutdownEventArgs.cs @@ -0,0 +1,17 @@ +// 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; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +public class RequestShutdownEventArgs : EventArgs +{ + public string Message { get; } + + public RequestShutdownEventArgs(string message) + { + Message = message; + } +} diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs index 44e6d704f92c4..fcf8172d597cb 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs @@ -4,10 +4,12 @@ using System; using System.Composition; -using System.Linq; +using System.IO; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer @@ -16,30 +18,36 @@ namespace Microsoft.CodeAnalysis.LanguageServer internal class CSharpVisualBasicLanguageServerFactory : ILanguageServerFactory { private readonly AbstractLspServiceProvider _lspServiceProvider; - private readonly IAsynchronousOperationListenerProvider _listenerProvider; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpVisualBasicLanguageServerFactory( - CSharpVisualBasicLspServiceProvider lspServiceProvider, - IAsynchronousOperationListenerProvider listenerProvider) + CSharpVisualBasicLspServiceProvider lspServiceProvider) { _lspServiceProvider = lspServiceProvider; - _listenerProvider = listenerProvider; } - public ILanguageServerTarget Create( + public AbstractLanguageServer Create( JsonRpc jsonRpc, ICapabilitiesProvider capabilitiesProvider, - ILspLogger logger) + WellKnownLspServerKinds serverKind, + ILspServiceLogger logger) { - return new LanguageServerTarget( - _lspServiceProvider, jsonRpc, + var server = new RoslynLanguageServer( + _lspServiceProvider, + jsonRpc, capabilitiesProvider, - _listenerProvider, logger, ProtocolConstants.RoslynLspLanguages, - WellKnownLspServerKinds.CSharpVisualBasicLspServer); + serverKind); + + return server; + } + + public AbstractLanguageServer Create(Stream input, Stream output, ICapabilitiesProvider capabilitiesProvider, ILspServiceLogger logger) + { + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(output, input)); + return Create(jsonRpc, capabilitiesProvider, WellKnownLspServerKinds.CSharpVisualBasicLspServer, logger); } } } diff --git a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs index 7bebe2e83649e..060efb65e254a 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs @@ -60,56 +60,73 @@ public static ImmutableArray GetDocumentIds(this Solution solution, public static Document? GetDocument(this Solution solution, TextDocumentIdentifier documentIdentifier) { var documents = solution.GetDocuments(documentIdentifier.Uri); - if (documents.Length == 0) - { - return null; - } - - return documents.FindDocumentInProjectContext(documentIdentifier); + return documents.Length == 0 + ? null + : documents.FindDocumentInProjectContext(documentIdentifier, (sln, id) => sln.GetRequiredDocument(id)); } - public static Document FindDocumentInProjectContext(this ImmutableArray documents, TextDocumentIdentifier documentIdentifier) + private static T FindItemInProjectContext( + ImmutableArray items, + TextDocumentIdentifier itemIdentifier, + Func projectIdGetter, + Func defaultGetter) { - if (documents.Length > 1) + if (items.Length > 1) { // We have more than one document; try to find the one that matches the right context - if (documentIdentifier is VSTextDocumentIdentifier vsDocumentIdentifier && vsDocumentIdentifier.ProjectContext != null) + if (itemIdentifier is VSTextDocumentIdentifier vsDocumentIdentifier && vsDocumentIdentifier.ProjectContext != null) { var projectId = ProtocolConversions.ProjectContextToProjectId(vsDocumentIdentifier.ProjectContext); - var matchingDocument = documents.FirstOrDefault(d => d.Project.Id == projectId); + var matchingItem = items.FirstOrDefault(d => projectIdGetter(d) == projectId); - if (matchingDocument != null) + if (matchingItem != null) { - return matchingDocument; + return matchingItem; } } else { - // We were not passed a project context. This can happen when the LSP powered NavBar is not enabled. - // This branch should be removed when we're using the LSP based navbar in all scenarios. - - var solution = documents.First().Project.Solution; - // Lookup which of the linked documents is currently active in the workspace. - var documentIdInCurrentContext = solution.Workspace.GetDocumentIdInCurrentContext(documents.First().Id); - return solution.GetRequiredDocument(documentIdInCurrentContext); + return defaultGetter(); } } - // We either have only one document or have multiple, but none of them matched our context. In the + // We either have only one item or have multiple, but none of them matched our context. In the // latter case, we'll just return the first one arbitrarily since this might just be some temporary mis-sync // of client and server state. - return documents[0]; + return items[0]; + } + + public static T FindDocumentInProjectContext(this ImmutableArray documents, TextDocumentIdentifier documentIdentifier, Func documentGetter) where T : TextDocument + { + return FindItemInProjectContext(documents, documentIdentifier, projectIdGetter: (item) => item.Project.Id, defaultGetter: () => + { + // We were not passed a project context. This can happen when the LSP powered NavBar is not enabled. + // This branch should be removed when we're using the LSP based navbar in all scenarios. + + var solution = documents.First().Project.Solution; + // Lookup which of the linked documents is currently active in the workspace. + var documentIdInCurrentContext = solution.Workspace.GetDocumentIdInCurrentContext(documents.First().Id); + return documentGetter(solution, documentIdInCurrentContext); + }); } public static Project? GetProject(this Solution solution, TextDocumentIdentifier projectIdentifier) - => solution.Projects.Where(project => project.FilePath == projectIdentifier.Uri.LocalPath).SingleOrDefault(); + { + var projects = solution.Projects.Where(project => project.FilePath == projectIdentifier.Uri.LocalPath).ToImmutableArray(); + return !projects.Any() + ? null + : FindItemInProjectContext(projects, projectIdentifier, projectIdGetter: (item) => item.Id, defaultGetter: () => projects[0]); + } public static TextDocument? GetAdditionalDocument(this Solution solution, TextDocumentIdentifier documentIdentifier) { var documentIds = GetDocumentIds(solution, documentIdentifier.Uri); // We don't call GetRequiredAdditionalDocument as the id could be referring to a regular document. - return documentIds.Select(solution.GetAdditionalDocument).WhereNotNull().SingleOrDefault(); + var additionalDocuments = documentIds.Select(solution.GetAdditionalDocument).WhereNotNull().ToImmutableArray(); + return !additionalDocuments.Any() + ? null + : additionalDocuments.FindDocumentInProjectContext(documentIdentifier, (sln, id) => sln.GetRequiredAdditionalDocument(id)); } public static async Task GetPositionFromLinePositionAsync(this TextDocument document, LinePosition linePosition, CancellationToken cancellationToken) diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 21762764f6b94..3e7050e3425aa 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -660,6 +660,26 @@ public static ProjectId ProjectContextToProjectId(LSP.VSProjectContext projectCo debugName: projectContext.Id.Substring(delimiter + 1)); } + public static LSP.VSProjectContext ProjectToProjectContext(Project project) + { + var projectContext = new LSP.VSProjectContext + { + Id = ProjectIdToProjectContextId(project.Id), + Label = project.Name + }; + + if (project.Language == LanguageNames.CSharp) + { + projectContext.Kind = LSP.VSProjectKind.CSharp; + } + else if (project.Language == LanguageNames.VisualBasic) + { + projectContext.Kind = LSP.VSProjectKind.VisualBasic; + } + + return projectContext; + } + public static async Task GetFormattingOptionsAsync( LSP.FormattingOptions? options, Document document, diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 27eb2eb029d30..c0e985606605b 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -103,6 +103,9 @@ public async Task GetMostSevereFixAsync( document, range, GetShouldIncludeDiagnosticPredicate(document, priority), includeSuppressedDiagnostics: false, priority, cancellationToken).ConfigureAwait(false); + var buildOnlyDiagnosticsService = document.Project.Solution.Services.GetRequiredService(); + allDiagnostics.AddRange(buildOnlyDiagnosticsService.GetBuildOnlyDiagnostics(document.Id)); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var spanToDiagnostics = ConvertToMap(text, allDiagnostics); @@ -177,26 +180,37 @@ public async IAsyncEnumerable StreamFixesAsync( includeCompilerDiagnostics: true, includeSuppressedDiagnostics: includeSuppressionFixes, priority: priority, addOperationScope: addOperationScope, cancellationToken: cancellationToken).ConfigureAwait(false); - if (diagnostics.IsEmpty) - yield break; + var buildOnlyDiagnosticsService = document.Project.Solution.Services.GetRequiredService(); + var buildOnlyDiagnostics = buildOnlyDiagnosticsService.GetBuildOnlyDiagnostics(document.Id); - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var spanToDiagnostics = ConvertToMap(text, diagnostics); + if (diagnostics.IsEmpty && buildOnlyDiagnostics.IsEmpty) + yield break; - // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. - if (priority != CodeActionRequestPriority.Lowest) + if (!diagnostics.IsEmpty) { - await foreach (var collection in StreamFixesAsync( - document, spanToDiagnostics, fixAllForInSpan: false, - priority, fallbackOptions, isBlocking, addOperationScope, cancellationToken).ConfigureAwait(false)) + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var spanToDiagnostics = ConvertToMap(text, diagnostics); + + // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. + if (priority != CodeActionRequestPriority.Lowest) { - yield return collection; + await foreach (var collection in StreamFixesAsync( + document, spanToDiagnostics, fixAllForInSpan: false, + priority, fallbackOptions, isBlocking, addOperationScope, cancellationToken).ConfigureAwait(false)) + { + yield return collection; + } } } // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive if (document.Project.Solution.WorkspaceKind != WorkspaceKind.Interactive && includeSuppressionFixes) { + // For build-only diagnostics, we support configuration/suppression fixes. + diagnostics = diagnostics.AddRange(buildOnlyDiagnostics); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var spanToDiagnostics = ConvertToMap(text, diagnostics); + // Ensure that we do not register duplicate configuration fixes. using var _2 = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); foreach (var (span, diagnosticList) in spanToDiagnostics) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs index 02b004e1a3b5d..ccadd852de578 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs @@ -370,7 +370,7 @@ async Task VerifyDiagnosticLocationAsync(string id, Location location) break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index d41c2dba6d23c..5ec74f649933c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -60,7 +60,7 @@ internal partial class DiagnosticIncrementalAnalyzer } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } static bool IsAnalyzerEnabledForDocument( @@ -154,7 +154,7 @@ private static async Task ComputeDocumentAnalysisDataAsync } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } @@ -249,7 +249,7 @@ private async Task GetProjectAnalysisDataAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } @@ -332,7 +332,7 @@ private async Task> buildDiagnostics) + { + foreach (var diagnostic in buildDiagnostics.Values.SelectMany(v => v)) + { + Debug.Assert(diagnostic.IsBuildDiagnostic()); + } + } + private ImmutableDictionary CreateAnalysisResults( Project project, ImmutableArray stateSets, ImmutableArray diagnostics) { diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 0b37808cf1e63..fc7fb6e84d2e9 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -211,7 +211,7 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Local functions diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 212328a535447..62374c17e45fa 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -100,7 +100,7 @@ private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKi } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } void PersistAndRaiseDiagnosticsIfNeeded(DocumentAnalysisData result, StateSet stateSet) @@ -176,7 +176,7 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs index 418a2f3011c3d..02d2ebfad5121 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Xml.Serialization; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeStyle; @@ -21,18 +22,17 @@ internal static class CodeActionOptionsStorage new("FormattingOptions", "WrappingColumn", CodeActionOptions.DefaultWrappingColumn); public static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => new( - cleanupOptions: globalOptions.GetCodeCleanupOptions(languageServices), - codeGenerationOptions: globalOptions.GetCodeGenerationOptions(languageServices), - codeStyleOptions: globalOptions.GetCodeStyleOptions(languageServices)) + => new() { + CleanupOptions = globalOptions.GetCodeCleanupOptions(languageServices), + CodeGenerationOptions = globalOptions.GetCodeGenerationOptions(languageServices), + CodeStyleOptions = globalOptions.GetCodeStyleOptions(languageServices), SearchOptions = globalOptions.GetSymbolSearchOptions(languageServices.Language), ImplementTypeOptions = globalOptions.GetImplementTypeOptions(languageServices.Language), ExtractMethodOptions = globalOptions.GetExtractMethodOptions(languageServices.Language), HideAdvancedMembers = globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, languageServices.Language), WrappingColumn = globalOptions.GetOption(WrappingColumn, languageServices.Language), ConditionalExpressionWrappingLength = globalOptions.GetOption(ConditionalExpressionWrappingLength, languageServices.Language), - EnableConvertToRecord = globalOptions.GetOption(EnableConvertToRecord), }; internal static CodeActionOptionsProvider GetCodeActionOptionsProvider(this IGlobalOptionService globalOptions) @@ -45,9 +45,5 @@ internal static CodeActionOptionsProvider GetCodeActionOptionsProvider(this IGlo "UseConditionalExpressionOptions", "ConditionalExpressionWrappingLength", CodeActionOptions.DefaultConditionalExpressionWrappingLength, storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ConditionalExpressionWrappingLength")); - - public static Option2 EnableConvertToRecord = new( - "ConvertToRecord", "EnableConvertToRecord", defaultValue: false, - storageLocation: new FeatureFlagStorageLocation("Roslyn.EnableConvertToRecord")); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CodeCleanupOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CodeCleanupOptionsStorage.cs index 148aacc336ccb..f44e9d9e35bb7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CodeCleanupOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CodeCleanupOptionsStorage.cs @@ -19,10 +19,10 @@ public static ValueTask GetCodeCleanupOptionsAsync(this Docu => document.GetCodeCleanupOptionsAsync(globalOptions.GetCodeCleanupOptions(document.Project.Services), cancellationToken); public static CodeCleanupOptions GetCodeCleanupOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => new( - globalOptions.GetSyntaxFormattingOptions(languageServices), - globalOptions.GetSimplifierOptions(languageServices)) + => new() { + FormattingOptions = globalOptions.GetSyntaxFormattingOptions(languageServices), + SimplifierOptions = globalOptions.GetSimplifierOptions(languageServices), AddImportOptions = globalOptions.GetAddImportPlacementOptions(languageServices), DocumentFormattingOptions = globalOptions.GetDocumentFormattingOptions(languageServices.Language) }; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CodeGenerationOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CodeGenerationOptionsStorage.cs index e935a82ab7fc4..9922d93d63bb7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CodeGenerationOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CodeGenerationOptionsStorage.cs @@ -36,8 +36,16 @@ public static CodeGenerationOptions GetCodeGenerationOptions(this IGlobalOptionS => languageServices.GetRequiredService().GetOptions(globalOptions); public static CodeAndImportGenerationOptions GetCodeAndImportGenerationOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => new(globalOptions.GetCodeGenerationOptions(languageServices), globalOptions.GetAddImportPlacementOptions(languageServices)); + => new() + { + GenerationOptions = globalOptions.GetCodeGenerationOptions(languageServices), + AddImportOptions = globalOptions.GetAddImportPlacementOptions(languageServices) + }; public static CleanCodeGenerationOptions GetCleanCodeGenerationOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => new(globalOptions.GetCodeGenerationOptions(languageServices), globalOptions.GetCodeCleanupOptions(languageServices)); + => new() + { + GenerationOptions = globalOptions.GetCodeGenerationOptions(languageServices), + CleanupOptions = globalOptions.GetCodeCleanupOptions(languageServices) + }; } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs index b45ea19579de5..f5a7955c5a4fc 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs @@ -22,8 +22,9 @@ public static ExtractMethodOptions GetExtractMethodOptions(this IGlobalOptionSer }; public static ExtractMethodGenerationOptions GetExtractMethodGenerationOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => new(globalOptions.GetCodeGenerationOptions(languageServices)) + => new() { + CodeGenerationOptions = globalOptions.GetCodeGenerationOptions(languageServices), ExtractOptions = globalOptions.GetExtractMethodOptions(languageServices.Language), AddImportOptions = globalOptions.GetAddImportPlacementOptions(languageServices), LineFormattingOptions = globalOptions.GetLineFormattingOptions(languageServices.Language) diff --git a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs index 31ac67f762069..b45f577330f83 100644 --- a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.TodoComments; namespace Microsoft.CodeAnalysis.TaskList { @@ -60,27 +59,9 @@ private ImmutableArray GetDescriptors(ImmutableArray(); - if (todoService != null) - return new TodoCommentServiceWrapper(todoService); -#pragma warning restore CS0618 // Type or member is obsolete -#pragma warning restore CS0612 // Type or member is obsolete - - var todoDataService = document.GetLanguageService(); - if (todoDataService != null) - return todoDataService; - - return null; - } - public override async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) { - var service = GetTaskListService(document); + var service = document.GetLanguageService(); if (service == null) return; @@ -108,24 +89,5 @@ public override async Task AnalyzeSyntaxAsync(Document document, InvocationReaso // Now inform VS about this new information await _listener.ReportTaskListItemsAsync(document.Id, items, cancellationToken).ConfigureAwait(false); } - - [Obsolete] - private sealed class TodoCommentServiceWrapper : ITaskListService - { - private readonly ITodoCommentService _todoService; - - public TodoCommentServiceWrapper(ITodoCommentService todoService) - { - _todoService = todoService; - } - - public async Task> GetTaskListItemsAsync( - Document document, ImmutableArray descriptors, CancellationToken cancellationToken) - { - var comments = await _todoService.GetTodoCommentsAsync( - document, descriptors.SelectAsArray(d => new TodoCommentDescriptor(d.Text, d.Priority)), cancellationToken).ConfigureAwait(false); - return await TodoComment.ConvertAsync(document, comments, cancellationToken).ConfigureAwait(false); - } - } } } diff --git a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs index 804f9fb106f4e..0db20d94fd2f1 100644 --- a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs +++ b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs @@ -86,7 +86,7 @@ public ValueTask ReportTaskListItemsAsync(DocumentId documentId, ImmutableArray< catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { // report NFW before returning back to the remote process - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index aaa4defa97a12..9b0a804daf2ca 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -418,7 +418,7 @@ private static string GetFixCategory(DiagnosticSeverity severity) case DiagnosticSeverity.Error: return UnifiedPredefinedSuggestedActionCategoryNames.ErrorFix; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Breakpoints/ValidateBreakableRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Breakpoints/ValidateBreakableRangeHandler.cs index 568fb864b3dcd..6c2318cdcfbf3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Breakpoints/ValidateBreakableRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Breakpoints/ValidateBreakableRangeHandler.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -17,7 +18,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(ValidateBreakableRangeHandler)), Shared] [Method(LSP.VSInternalMethods.TextDocumentValidateBreakableRangeName)] - internal sealed class ValidateBreakableRangeHandler : IRequestHandler + internal sealed class ValidateBreakableRangeHandler : ILspServiceDocumentRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -28,13 +29,12 @@ public ValidateBreakableRangeHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.VSInternalValidateBreakableRangeParams request) + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.VSInternalValidateBreakableRangeParams request) => request.TextDocument; public async Task HandleRequestAsync(LSP.VSInternalValidateBreakableRangeParams request, RequestContext context, CancellationToken cancellationToken) { - var document = context.Document; - Contract.ThrowIfNull(document); + var document = context.GetRequiredDocument(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var span = ProtocolConversions.RangeToTextSpan(request.Range, text); diff --git a/src/Features/LanguageServer/Protocol/Handler/ClientCapabilitiesManager.cs b/src/Features/LanguageServer/Protocol/Handler/ClientCapabilitiesManager.cs new file mode 100644 index 0000000000000..32381f69a0a12 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ClientCapabilitiesManager.cs @@ -0,0 +1,41 @@ +// 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 Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +/// +/// +/// This is not actually stateless, but we need to be sure it doesn't re-construct each time it is retrieved +/// and the only state will be wiped out on Server startup +internal class ClientCapabilitiesManager : IClientCapabilitiesManager +{ + public ClientCapabilitiesManager() + { + } + + private ClientCapabilities? _clientCapabilities; + + public ClientCapabilities GetClientCapabilities() + { + if (_clientCapabilities is null) + { + throw new InvalidOperationException($"Tried to get required {nameof(ClientCapabilities)} before it was set"); + } + + return _clientCapabilities; + } + + public void SetClientCapabilities(ClientCapabilities clientCapabilities) + { + _clientCapabilities = clientCapabilities; + } + + public ClientCapabilities? TryGetClientCapabilities() + { + return _clientCapabilities; + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs index cb3b812c1dd0f..c362a9c410414 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs @@ -8,14 +8,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Commands { - internal abstract class AbstractExecuteWorkspaceCommandHandler : IRequestHandler + internal abstract class AbstractExecuteWorkspaceCommandHandler : ILspServiceDocumentRequestHandler { public abstract string Command { get; } public abstract bool MutatesSolutionState { get; } public abstract bool RequiresLSPSolution { get; } - public abstract TextDocumentIdentifier? GetTextDocumentIdentifier(ExecuteCommandParams request); + public abstract TextDocumentIdentifier GetTextDocumentIdentifier(ExecuteCommandParams request); public abstract Task HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken); diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs index 05e86a424d7d1..4c729a7fcc5dd 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - internal abstract class AbstractGoToDefinitionHandler : IRequestHandler + internal abstract class AbstractGoToDefinitionHandler : ILspServiceDocumentRequestHandler { private readonly IMetadataAsSourceFileService _metadataAsSourceFileService; private readonly IGlobalOptionService _globalOptions; @@ -32,7 +32,7 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; public abstract Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken); diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index ce9850c157f24..634738794bbf8 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -12,12 +12,25 @@ using Microsoft.CodeAnalysis.LanguageServer.Features.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { + internal abstract class AbstractDocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler, ITextDocumentIdentifierHandler + where TDiagnosticsParams : IPartialResultParams + { + public AbstractDocumentPullDiagnosticHandler( + IDiagnosticAnalyzerService diagnosticAnalyzerService, + EditAndContinueDiagnosticUpdateSource editAndContinueDiagnosticUpdateSource, + IGlobalOptionService globalOptions) : base(diagnosticAnalyzerService, editAndContinueDiagnosticUpdateSource, globalOptions) + { + } + + public abstract LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(TDiagnosticsParams diagnosticsParams); + } /// /// Root type for both document and workspace diagnostic pull requests. @@ -25,7 +38,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics /// The LSP input param type /// The LSP type that is reported via IProgress /// The LSP type that is returned on completion of the request. - internal abstract partial class AbstractPullDiagnosticHandler : IRequestHandler where TDiagnosticsParams : IPartialResultParams + internal abstract partial class AbstractPullDiagnosticHandler : ILspServiceRequestHandler where TDiagnosticsParams : IPartialResultParams { /// /// Diagnostic mode setting for Razor. This should always be as there is no push support in Razor. @@ -75,8 +88,6 @@ protected AbstractPullDiagnosticHandler( _versionedCache = new(this.GetType().Name); } - public abstract TextDocumentIdentifier? GetTextDocumentIdentifier(TDiagnosticsParams diagnosticsParams); - /// /// Retrieve the previous results we reported. Used so we can avoid resending data for unchanged files. Also /// used so we can report which documents were removed and can have all their diagnostics cleared. @@ -113,6 +124,7 @@ protected AbstractPullDiagnosticHandler( public async Task HandleRequestAsync( TDiagnosticsParams diagnosticsParams, RequestContext context, CancellationToken cancellationToken) { + var clientCapabilities = context.GetRequiredClientCapabilities(); context.TraceInformation($"{this.GetType()} started getting diagnostics"); var diagnosticMode = GetDiagnosticMode(context); @@ -157,11 +169,11 @@ protected AbstractPullDiagnosticHandler( if (newResultId != null) { progress.Report(await ComputeAndReportCurrentDiagnosticsAsync( - context, diagnosticSource, newResultId, context.ClientCapabilities, cancellationToken).ConfigureAwait(false)); + context, diagnosticSource, newResultId, clientCapabilities, cancellationToken).ConfigureAwait(false)); } else { - context.TraceInformation($"Diagnostics were unchanged for document: {diagnosticSource.GetUri()}"); + context.TraceInformation($"Diagnostics were unchanged for {diagnosticSource.GetDocumentIdentifier().Uri} in {diagnosticSource.GetProject().Name}"); // Nothing changed between the last request and this one. Report a (null-diagnostics, // same-result-id) response to the client as that means they should just preserve the current @@ -251,12 +263,12 @@ private async Task ComputeAndReportCurrentDiagnosticsAsync( { using var _ = ArrayBuilder.GetInstance(out var result); var diagnostics = await diagnosticSource.GetDiagnosticsAsync(DiagnosticAnalyzerService, context, cancellationToken).ConfigureAwait(false); - context.TraceInformation($"Found {diagnostics.Length} diagnostics for {diagnosticSource.GetUri()}"); + context.TraceInformation($"Found {diagnostics.Length} diagnostics for {diagnosticSource.GetDocumentIdentifier().Uri} in {diagnosticSource.GetProject().Name}"); foreach (var diagnostic in diagnostics) result.AddRange(ConvertDiagnostic(diagnosticSource, diagnostic, clientCapabilities)); - return CreateReport(new LSP.TextDocumentIdentifier { Uri = diagnosticSource.GetUri() }, result.ToArray(), resultId); + return CreateReport(diagnosticSource.GetDocumentIdentifier(), result.ToArray(), resultId); } private void HandleRemovedDocuments(RequestContext context, ImmutableArray removedPreviousResults, BufferedProgress progress) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs index 3ce474d28195d..5f4d90f1da8c1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractDocumentDiagnosticSource.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.TaskList; +using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; @@ -30,7 +31,8 @@ protected AbstractDocumentDiagnosticSource(TDocument document) public ProjectOrDocumentId GetId() => new(Document.Id); public Project GetProject() => Document.Project; - public Uri GetUri() => Document.GetURI(); + public TextDocumentIdentifier GetDocumentIdentifier() + => new VSTextDocumentIdentifier { ProjectContext = ProtocolConversions.ProjectToProjectContext(Document.Project), Uri = Document.GetURI() }; protected abstract bool IncludeTaskListItems { get; } protected abstract bool IncludeStandardDiagnostics { get; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/IDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/IDiagnosticSource.cs index 88f54df22ea36..b1204b0fcee68 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/IDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/IDiagnosticSource.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; @@ -18,7 +19,7 @@ internal interface IDiagnosticSource { Project GetProject(); ProjectOrDocumentId GetId(); - Uri GetUri(); + TextDocumentIdentifier GetDocumentIdentifier(); Task> GetDiagnosticsAsync( IDiagnosticAnalyzerService diagnosticAnalyzerService, diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs index caddfaa179eef..d407708b36bcf 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; @@ -15,11 +16,10 @@ internal sealed record class ProjectDiagnosticSource(Project Project) : IDiagnos { public ProjectOrDocumentId GetId() => new(Project.Id); public Project GetProject() => Project; - - public Uri GetUri() + public TextDocumentIdentifier GetDocumentIdentifier() { Contract.ThrowIfNull(Project.FilePath); - return ProtocolConversions.GetUriFromFilePath(Project.FilePath); + return new VSTextDocumentIdentifier { ProjectContext = ProtocolConversions.ProjectToProjectContext(Project), Uri = ProtocolConversions.GetUriFromFilePath(Project.FilePath) }; } public async Task> GetDiagnosticsAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index ea7657391be27..ee760624f59f2 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { [Method(VSInternalMethods.DocumentPullDiagnosticName)] - internal partial class DocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler + internal partial class DocumentPullDiagnosticHandler : AbstractDocumentPullDiagnosticHandler { public DocumentPullDiagnosticHandler( IDiagnosticAnalyzerService analyzerService, @@ -80,19 +80,20 @@ internal static ImmutableArray GetDiagnosticSources(RequestCo // // Only consider open documents here (and only closed ones in the WorkspacePullDiagnosticHandler). Each // handler treats those as separate worlds that they are responsible for. - if (context.Document == null) + var document = context.Document; + if (document is null) { context.TraceInformation("Ignoring diagnostics request because no document was provided"); return ImmutableArray.Empty; } - if (!context.IsTracking(context.Document.GetURI())) + if (!context.IsTracking(document.GetURI())) { - context.TraceWarning($"Ignoring diagnostics request for untracked document: {context.Document.GetURI()}"); + context.TraceWarning($"Ignoring diagnostics request for untracked document: {document.GetURI()}"); return ImmutableArray.Empty; } - return ImmutableArray.Create(new DocumentDiagnosticSource(context.Document)); + return ImmutableArray.Create(new DocumentDiagnosticSource(document)); } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs index f17cdacd1f9cc..1a712a381f841 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs @@ -4,13 +4,11 @@ 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.EditAndContinue; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; @@ -25,7 +23,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental using DocumentDiagnosticPartialReport = SumType; [Method(ExperimentalMethods.TextDocumentDiagnostic)] -internal class ExperimentalDocumentPullDiagnosticsHandler : AbstractPullDiagnosticHandler +internal class ExperimentalDocumentPullDiagnosticsHandler : AbstractDocumentPullDiagnosticHandler { public ExperimentalDocumentPullDiagnosticsHandler( IDiagnosticAnalyzerService analyzerService, @@ -35,7 +33,7 @@ public ExperimentalDocumentPullDiagnosticsHandler( { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentDiagnosticParams diagnosticsParams) => diagnosticsParams.TextDocument; + public override TextDocumentIdentifier GetTextDocumentIdentifier(DocumentDiagnosticParams diagnosticsParams) => diagnosticsParams.TextDocument; protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) { diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs index 3806b68f12a2f..58454ca5290a6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs @@ -28,8 +28,6 @@ public ExperimentalWorkspacePullDiagnosticsHandler( { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(WorkspaceDiagnosticParams diagnosticsParams) => null; - protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) { return ConvertTags(diagnosticData, potentialDuplicate: false); diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index a76d64c2f9ede..fa58f32cd2d7d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -27,9 +27,6 @@ public WorkspacePullDiagnosticHandler(IDiagnosticAnalyzerService analyzerService { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceDiagnosticsParams request) - => null; - protected override VSInternalWorkspaceDiagnosticReport CreateReport(TextDocumentIdentifier identifier, VisualStudio.LanguageServer.Protocol.Diagnostic[]? diagnostics, string? resultId) => new VSInternalWorkspaceDiagnosticReport { diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs index af8e962972bae..6a8f66ffbfc65 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -15,7 +16,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { [ExportCSharpVisualBasicStatelessLspService(typeof(DidChangeHandler)), Shared] [Method(LSP.Methods.TextDocumentDidChangeName)] - internal class DidChangeHandler : IRequestHandler + internal class DidChangeHandler : ILspServiceDocumentRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -26,7 +27,7 @@ public DidChangeHandler() public bool MutatesSolutionState => true; public bool RequiresLSPSolution => false; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DidChangeTextDocumentParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.DidChangeTextDocumentParams request) => request.TextDocument; public Task HandleRequestAsync(LSP.DidChangeTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs index a2714c3779b83..9de37abd39153 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs @@ -7,14 +7,15 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { [ExportCSharpVisualBasicStatelessLspService(typeof(DidCloseHandler)), Shared] [Method(LSP.Methods.TextDocumentDidCloseName)] - internal class DidCloseHandler : IRequestHandler + internal class DidCloseHandler : ILspServiceNotificationHandler, ITextDocumentIdentifierHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -25,16 +26,16 @@ public DidCloseHandler() public bool MutatesSolutionState => true; public bool RequiresLSPSolution => false; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DidCloseTextDocumentParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.DidCloseTextDocumentParams request) => request.TextDocument; - public Task HandleRequestAsync(LSP.DidCloseTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) + public Task HandleNotificationAsync(LSP.DidCloseTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) { // GetTextDocumentIdentifier returns null to avoid creating the solution, so the queue is not able to log the uri. context.TraceInformation($"didClose for {request.TextDocument.Uri}"); context.StopTracking(request.TextDocument.Uri); - return SpecializedTasks.Default(); + return Task.CompletedTask; } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index eb02cb25745b5..51f9c141f66af 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -15,7 +16,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { [ExportCSharpVisualBasicStatelessLspService(typeof(DidOpenHandler)), Shared] [Method(LSP.Methods.TextDocumentDidOpenName)] - internal class DidOpenHandler : IRequestHandler + internal class DidOpenHandler : ILspServiceNotificationHandler, ITextDocumentIdentifierHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -26,19 +27,21 @@ public DidOpenHandler() public bool MutatesSolutionState => true; public bool RequiresLSPSolution => false; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DidOpenTextDocumentParams request) => new() { Uri = request.TextDocument.Uri }; + public Uri GetTextDocumentIdentifier(LSP.DidOpenTextDocumentParams request) => request.TextDocument.Uri; - public Task HandleRequestAsync(LSP.DidOpenTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) + public Task HandleNotificationAsync(LSP.DidOpenTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) { // GetTextDocumentIdentifier returns null to avoid creating the solution, so the queue is not able to log the uri. 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.OpenDocumentChecksumAlgorithm); context.StartTracking(request.TextDocument.Uri, sourceText); - return SpecializedTasks.Default(); + return Task.CompletedTask; } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs index 683fe991822cc..6c8a52aabff07 100644 --- a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(FoldingRangesHandler)), Shared] [Method(Methods.TextDocumentFoldingRangeName)] - internal sealed class FoldingRangesHandler : IRequestHandler + internal sealed class FoldingRangesHandler : ILspServiceDocumentRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -32,12 +32,12 @@ public FoldingRangesHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public TextDocumentIdentifier? GetTextDocumentIdentifier(FoldingRangeParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(FoldingRangeParams request) => request.TextDocument; public async Task HandleRequestAsync(FoldingRangeParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; - if (document == null) + if (document is null) return null; var blockStructureService = document.Project.Services.GetService(); diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index bfcbab1398bce..2d9859a4d028c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.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 System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -12,13 +11,12 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - internal abstract class AbstractFormatDocumentHandlerBase : IRequestHandler + internal abstract class AbstractFormatDocumentHandlerBase : ILspServiceDocumentRequestHandler { public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; @@ -31,7 +29,7 @@ internal abstract class AbstractFormatDocumentHandlerBase HandleRequestAsync(RequestType request, RequestContext context, CancellationToken cancellationToken); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs index 23ddc0bbf788e..f95e6f68c3c2e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs @@ -25,7 +25,7 @@ public FormatDocumentHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; + public override LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( LSP.DocumentFormattingParams request, diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 71550efcdceee..adeabd78172f9 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -23,7 +23,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(FormatDocumentOnTypeHandler)), Shared] [Method(Methods.TextDocumentOnTypeFormattingName)] - internal sealed class FormatDocumentOnTypeHandler : IRequestHandler + internal sealed class FormatDocumentOnTypeHandler : ILspServiceDocumentRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -37,7 +37,7 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentOnTypeFormattingParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentOnTypeFormattingParams request) => request.TextDocument; public async Task HandleRequestAsync( DocumentOnTypeFormattingParams request, @@ -45,7 +45,7 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) CancellationToken cancellationToken) { var document = context.Document; - if (document == null) + if (document is null) return null; var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs index 381a93aab25f2..09a2991bfb2b5 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -25,7 +25,7 @@ public FormatDocumentRangeHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; + public override TextDocumentIdentifier GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( DocumentRangeFormattingParams request, diff --git a/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs index f07e08beec8ff..4b6428e51565c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(DocumentHighlightsHandler)), Shared] [Method(Methods.TextDocumentDocumentHighlightName)] - internal class DocumentHighlightsHandler : IRequestHandler + internal class DocumentHighlightsHandler : ILspServiceDocumentRequestHandler { private readonly IHighlightingService _highlightingService; private readonly IGlobalOptionService _globalOptions; @@ -38,7 +38,7 @@ public DocumentHighlightsHandler(IHighlightingService highlightingService, IGlob public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/Features/LanguageServer/Protocol/Handler/IClientCapabilitiesManager.cs b/src/Features/LanguageServer/Protocol/Handler/IClientCapabilitiesManager.cs new file mode 100644 index 0000000000000..9b7ce6e7e43aa --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/IClientCapabilitiesManager.cs @@ -0,0 +1,19 @@ +// 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.VisualStudio.LanguageServer.Protocol; + +#nullable enable + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler +{ + internal interface IClientCapabilitiesManager : ILspService + { + ClientCapabilities GetClientCapabilities(); + + ClientCapabilities? TryGetClientCapabilities(); + + void SetClientCapabilities(ClientCapabilities clientCapabilities); + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ILspServiceNotificationHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ILspServiceNotificationHandler.cs new file mode 100644 index 0000000000000..8e0774a425dc6 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ILspServiceNotificationHandler.cs @@ -0,0 +1,21 @@ +// 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.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +internal interface ILspServiceNotificationHandler : + ILspService, + INotificationHandler, + ISolutionRequiredHandler +{ +} + +internal interface ILspServiceNotificationHandler : + ILspService, + INotificationHandler, + ISolutionRequiredHandler +{ +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs new file mode 100644 index 0000000000000..ef593aaa44073 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs @@ -0,0 +1,22 @@ +// 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.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +internal interface ILspServiceRequestHandler : + ILspService, + IRequestHandler, + ISolutionRequiredHandler +{ +} + +internal interface ILspServiceDocumentRequestHandler : + ILspServiceRequestHandler, + ITextDocumentIdentifierHandler, + ISolutionRequiredHandler +{ +} diff --git a/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs b/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs deleted file mode 100644 index b7787edf04728..0000000000000 --- a/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs +++ /dev/null @@ -1,47 +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.Threading; -using System.Threading.Tasks; -using Microsoft.VisualStudio.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler -{ - /// - /// Top level type for LSP request handler. - /// - internal interface IRequestHandler : ILspService - { - /// - /// Whether or not the solution state on the server is modified - /// as a part of handling this request. - /// - bool MutatesSolutionState { get; } - - /// - /// Whether or not the handler execution queue should build a solution that represents the LSP - /// state of the world. If this property is not set will be - /// and will be , even if - /// doesn't return null. Handlers should still provide text document information if possible to - /// ensure the correct workspace is found and validated. - /// - bool RequiresLSPSolution { get; } - } - - internal interface IRequestHandler : IRequestHandler - { - /// - /// Gets the from the request, if the request provides one. - /// - TextDocumentIdentifier? GetTextDocumentIdentifier(RequestType request); - - /// - /// Handles an LSP request in the context of the supplied document and/or solutuion. - /// - /// The LSP request context, which should have been filled in with document information from if applicable. - /// A cancellation token that can be used to cancel the request processing. - /// The LSP response. - Task HandleRequestAsync(RequestType request, RequestContext context, CancellationToken cancellationToken); - } -} diff --git a/src/Features/LanguageServer/Protocol/Handler/ISolutionRequiredHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ISolutionRequiredHandler.cs new file mode 100644 index 0000000000000..d78c3f2ab5da3 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ISolutionRequiredHandler.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +internal interface ISolutionRequiredHandler +{ + bool RequiresLSPSolution { get; } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs index 3664c209edda7..9f1b865449f84 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; @@ -31,7 +30,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlineCompletions; /// [ExportCSharpVisualBasicStatelessLspService(typeof(InlineCompletionsHandler)), Shared] [Method(VSInternalMethods.TextDocumentInlineCompletionName)] -internal partial class InlineCompletionsHandler : IRequestHandler +internal partial class InlineCompletionsHandler : ILspServiceDocumentRequestHandler { /// /// The set of built in snippets from, typically found in @@ -58,17 +57,17 @@ public InlineCompletionsHandler(XmlSnippetParser xmlSnippetParser, IGlobalOption _globalOptions = globalOptions; } - public TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalInlineCompletionRequest request) + public TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalInlineCompletionRequest request) { return request.TextDocument; } public async Task HandleRequestAsync(VSInternalInlineCompletionRequest request, RequestContext context, CancellationToken cancellationToken) { - Contract.ThrowIfNull(context.Document); + var document = context.GetRequiredDocument(); // First get available snippets if any. - var snippetInfoService = context.Document.Project.GetRequiredLanguageService(); + var snippetInfoService = document.Project.GetRequiredLanguageService(); var snippetInfo = snippetInfoService.GetSnippetsIfAvailable(); if (!snippetInfo.Any()) { @@ -76,8 +75,8 @@ public InlineCompletionsHandler(XmlSnippetParser xmlSnippetParser, IGlobalOption } // Then attempt to get the word at the requested position. - var sourceText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var syntaxFactsService = context.Document.Project.GetRequiredLanguageService(); + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var syntaxFactsService = document.Project.GetRequiredLanguageService(); var linePosition = ProtocolConversions.PositionToLinePosition(request.Position); var position = sourceText.Lines.GetPosition(linePosition); if (!SnippetUtilities.TryGetWordOnLeft(position, sourceText, syntaxFactsService, out var wordOnLeft)) @@ -101,10 +100,10 @@ public InlineCompletionsHandler(XmlSnippetParser xmlSnippetParser, IGlobalOption } // Use the formatting options specified by the client to format the snippet. - var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, context.Document, _globalOptions, cancellationToken).ConfigureAwait(false); - var simplifierOptions = await context.Document.GetSimplifierOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, _globalOptions, cancellationToken).ConfigureAwait(false); + var simplifierOptions = await document.GetSimplifierOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); - var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, context.Document, sourceText, formattingOptions, simplifierOptions, cancellationToken).ConfigureAwait(false); + var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, document, sourceText, formattingOptions, simplifierOptions, cancellationToken).ConfigureAwait(false); return new VSInternalInlineCompletionList { diff --git a/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs b/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs index a96d3d1ee788f..f48d46d4e8d8d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs +++ b/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs @@ -2,28 +2,35 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CodeAnalysis.LanguageServer.Handler +using System; +using System.Threading; +using System.Threading.Tasks; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +internal class ClientLanguageServerManager : IClientLanguageServerManager { - using System; - using System.Threading; - using System.Threading.Tasks; - using StreamJsonRpc; + private readonly JsonRpc _jsonRpc; - internal class LanguageServerNotificationManager : ILanguageServerNotificationManager + public ClientLanguageServerManager(JsonRpc jsonRpc) { - private readonly JsonRpc _jsonRpc; - - public LanguageServerNotificationManager(JsonRpc jsonRpc) + if (jsonRpc is null) { - if (jsonRpc is null) - { - throw new ArgumentNullException(nameof(jsonRpc)); - } - - _jsonRpc = jsonRpc; + throw new ArgumentNullException(nameof(jsonRpc)); } - public async ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken) - => await _jsonRpc.NotifyAsync(methodName).ConfigureAwait(false); + _jsonRpc = jsonRpc; + } + + public Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken) + => _jsonRpc.InvokeAsync(methodName, @params); + + public async ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken) + => await _jsonRpc.NotifyAsync(methodName).ConfigureAwait(false); + + public async ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken) + { + await _jsonRpc.NotifyWithParameterObjectAsync(methodName, @params).ConfigureAwait(false); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs b/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs index 091829e570413..5eb1037238872 100644 --- a/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs +++ b/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs @@ -2,23 +2,15 @@ // 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 Microsoft.CommonLanguageServerProtocol.Framework; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +[MetadataAttribute] +internal class MethodAttribute : LanguageServerEndpointAttribute { - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class)] - internal class MethodAttribute : Attribute + public MethodAttribute(string method) : base(method) { - /// - /// Contains the method that this implements. - /// - public string Method { get; } - - public MethodAttribute(string method) - { - Method = method; - } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 9a00da65a639f..e0285081c4831 100644 --- a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(OnAutoInsertHandler)), Shared] [Method(LSP.VSInternalMethods.OnAutoInsertName)] - internal sealed class OnAutoInsertHandler : IRequestHandler + internal sealed class OnAutoInsertHandler : ILspServiceDocumentRequestHandler { private readonly ImmutableArray _csharpBraceCompletionServices; private readonly ImmutableArray _visualBasicBraceCompletionServices; @@ -47,7 +47,7 @@ public OnAutoInsertHandler( _globalOptions = globalOptions; } - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; + public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; public async Task HandleRequestAsync( LSP.VSInternalDocumentOnAutoInsertParams request, diff --git a/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs index b443b64d4c8a5..3fb63ca69e04d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(GetTextDocumentWithContextHandler)), Shared] [Method(VSMethods.GetProjectContextsName)] - internal class GetTextDocumentWithContextHandler : IRequestHandler + internal class GetTextDocumentWithContextHandler : ILspServiceDocumentRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -28,7 +28,7 @@ public GetTextDocumentWithContextHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new TextDocumentIdentifier { Uri = request.TextDocument.Uri }; + public TextDocumentIdentifier GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new TextDocumentIdentifier { Uri = request.TextDocument.Uri }; public Task HandleRequestAsync(VSGetProjectContextsParams request, RequestContext context, CancellationToken cancellationToken) { @@ -48,21 +48,7 @@ public GetTextDocumentWithContextHandler() foreach (var document in documents) { var project = document.Project; - var projectContext = new VSProjectContext - { - Id = ProtocolConversions.ProjectIdToProjectContextId(project.Id), - Label = project.Name - }; - - if (project.Language == LanguageNames.CSharp) - { - projectContext.Kind = VSProjectKind.CSharp; - } - else if (project.Language == LanguageNames.VisualBasic) - { - projectContext.Kind = VSProjectKind.VisualBasic; - } - + var projectContext = ProtocolConversions.ProjectToProjectContext(project); contexts.Add(projectContext); } diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs index d2e5eee6cfc9f..e8ba5b8a1d89a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs @@ -3,136 +3,161 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.LanguageServer.Handler.RequestExecutionQueue; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +/// +/// Context for requests handled by +/// +internal readonly struct RequestContext { /// - /// Context for requests handled by + /// This will be the for non-mutating requests because they're not allowed to change documents + /// + private readonly IDocumentChangeTracker _documentChangeTracker; + + /// + /// The client capabilities for the request. + /// + /// + /// Should only be null on the "initialize" request. + /// + private readonly ClientCapabilities? _clientCapabilities; + + /// + /// Contains the LSP text for all opened LSP documents from when this request was processed in the queue. + /// + /// + /// This is a snapshot of the source text that reflects the LSP text based on the order of this request in the queue. + /// It contains text that is consistent with all prior LSP text sync notifications, but LSP text sync requests + /// which are ordered after this one in the queue are not reflected here. + /// + private readonly ImmutableDictionary _trackedDocuments; + + private readonly ILspServices _lspServices; + + /// + /// The workspace this request is for, if applicable. This will be present if is + /// present. It will be if requiresLSPSolution is false. + /// + public readonly Workspace? Workspace; + + /// + /// The solution state that the request should operate on, if the handler requires an LSP solution, or otherwise + /// + public readonly Solution? Solution; + + /// + /// The document that the request is for, if applicable. This comes from the returned from the handler itself via a call to + /// . + /// + public readonly Document? Document; + + /// + /// The LSP server handling the request. + /// + public readonly WellKnownLspServerKinds ServerKind; + + /// + /// The method this request is targeting. + /// + public readonly string Method; + + /// + /// The languages supported by the server making the request. /// - internal readonly struct RequestContext + public readonly ImmutableArray SupportedLanguages; + + public readonly CancellationToken QueueCancellationToken; + + /// + /// Tracing object that can be used to log information about the status of requests. + /// + private readonly ILspLogger _logger; + + public RequestContext( + Workspace? workspace, + Solution? solution, + ILspLogger logger, + string method, + ClientCapabilities? clientCapabilities, + WellKnownLspServerKinds serverKind, + Document? document, + IDocumentChangeTracker documentChangeTracker, + ImmutableDictionary trackedDocuments, + ImmutableArray supportedLanguages, + ILspServices lspServices, + CancellationToken queueCancellationToken) + { + Workspace = workspace; + Document = document; + Solution = solution; + _clientCapabilities = clientCapabilities; + ServerKind = serverKind; + SupportedLanguages = supportedLanguages; + _documentChangeTracker = documentChangeTracker; + _logger = logger; + _trackedDocuments = trackedDocuments; + _lspServices = lspServices; + QueueCancellationToken = queueCancellationToken; + Method = method; + } + + public ClientCapabilities GetRequiredClientCapabilities() { - /// - /// This will be the for non-mutating requests because they're not allowed to change documents - /// - private readonly IDocumentChangeTracker _documentChangeTracker; - - /// - /// Contains the LSP text for all opened LSP documents from when this request was processed in the queue. - /// - /// - /// This is a snapshot of the source text that reflects the LSP text based on the order of this request in the queue. - /// It contains text that is consistent with all prior LSP text sync notifications, but LSP text sync requests - /// which are ordered after this one in the queue are not reflected here. - /// - private readonly ImmutableDictionary _trackedDocuments; - - private readonly LspServices _lspServices; - - /// - /// The workspace this request is for, if applicable. This will be present if is - /// present. It will be if requiresLSPSolution is false. - /// - public readonly Workspace? Workspace; - - /// - /// The solution state that the request should operate on, if the handler requires an LSP solution, or otherwise - /// - public readonly Solution? Solution; - - /// - /// The document that the request is for, if applicable. This comes from the returned from the handler itself via a call to . - /// - public readonly Document? Document; - - /// - /// The client capabilities for the request. - /// - public readonly ClientCapabilities ClientCapabilities; - - /// - /// The LSP server handling the request. - /// - public readonly WellKnownLspServerKinds ServerKind; - - /// - /// The languages supported by the server making the request. - /// - public readonly ImmutableArray SupportedLanguages; - - public readonly CancellationToken QueueCancellationToken; - - /// - /// Tracing object that can be used to log information about the status of requests. - /// - private readonly ILspLogger _logger; - - public RequestContext( - Workspace? workspace, - Solution? solution, - Document? document, - ILspLogger logger, - ClientCapabilities clientCapabilities, - WellKnownLspServerKinds serverKind, - IDocumentChangeTracker documentChangeTracker, - ImmutableDictionary trackedDocuments, - ImmutableArray supportedLanguages, - LspServices lspServices, - CancellationToken queueCancellationToken) + return _clientCapabilities is null + ? throw new ArgumentNullException($"{nameof(ClientCapabilities)} is null when it was required for {Method}") + : _clientCapabilities; + } + + public Document GetRequiredDocument() + { + return Document is null + ? throw new ArgumentNullException($"{nameof(Document)} is null when it was required for {Method}") + : Document; + } + + public static async Task CreateAsync( + bool mutatesSolutionState, + bool requiresLSPSolution, + TextDocumentIdentifier? textDocument, + WellKnownLspServerKinds serverKind, + ClientCapabilities? clientCapabilities, + ImmutableArray supportedLanguages, + ILspServices lspServices, + ILspLogger logger, + string method, + CancellationToken cancellationToken) + { + var lspWorkspaceManager = lspServices.GetRequiredService(); + var documentChangeTracker = mutatesSolutionState ? (IDocumentChangeTracker)lspWorkspaceManager : new NonMutatingDocumentChangeTracker(); + + // Retrieve the current LSP tracked text as of this request. + // This is safe as all creation of request contexts cannot happen concurrently. + var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); + + // If the handler doesn't need an LSP solution we do two important things: + // 1. We don't bother building the LSP solution for perf reasons + // 2. We explicitly don't give the handler a solution or document, even if we could + // so they're not accidentally operating on stale solution state. + RequestContext context; + if (!requiresLSPSolution) { - Workspace = workspace; - Solution = solution; - Document = document; - ClientCapabilities = clientCapabilities; - ServerKind = serverKind; - SupportedLanguages = supportedLanguages; - _documentChangeTracker = documentChangeTracker; - _logger = logger; - _trackedDocuments = trackedDocuments; - _lspServices = lspServices; - QueueCancellationToken = queueCancellationToken; + context = new RequestContext( + workspace: null, solution: null, logger: logger, method: method, clientCapabilities: clientCapabilities, serverKind: serverKind, document: null, + documentChangeTracker: documentChangeTracker, trackedDocuments: trackedDocuments, supportedLanguages: supportedLanguages, lspServices: lspServices, + queueCancellationToken: cancellationToken); } - - public static async Task CreateAsync( - bool requiresLSPSolution, - bool mutatesSolutionState, - TextDocumentIdentifier? textDocument, - WellKnownLspServerKinds serverKind, - ClientCapabilities clientCapabilities, - ImmutableArray supportedLanguages, - LspServices lspServices, - CancellationToken queueCancellationToken, - CancellationToken requestCancellationToken) + else { - var lspWorkspaceManager = lspServices.GetRequiredService(); - var logger = lspServices.GetRequiredService(); - var documentChangeTracker = mutatesSolutionState ? (IDocumentChangeTracker)lspWorkspaceManager : new NonMutatingDocumentChangeTracker(); - - // Retrieve the current LSP tracked text as of this request. - // This is safe as all creation of request contexts cannot happen concurrently. - var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); - - // If the handler doesn't need an LSP solution we do two important things: - // 1. We don't bother building the LSP solution for perf reasons - // 2. We explicitly don't give the handler a solution or document, even if we could - // so they're not accidentally operating on stale solution state. - if (!requiresLSPSolution) - { - return new RequestContext( - workspace: null, solution: null, document: null, logger, clientCapabilities, serverKind, - documentChangeTracker, trackedDocuments, supportedLanguages, lspServices, queueCancellationToken); - } - Workspace? workspace = null; Solution? solution = null; Document? document = null; @@ -142,81 +167,94 @@ public RequestContext( // There are certain cases where we may be asked for a document that does not exist (for example a // document is removed) For example, document pull diagnostics can ask us after removal to clear // diagnostics for a document. - (workspace, solution, document) = await lspWorkspaceManager.GetLspDocumentInfoAsync(textDocument, requestCancellationToken).ConfigureAwait(false); + (workspace, solution, document) = await lspWorkspaceManager.GetLspDocumentInfoAsync(textDocument, cancellationToken).ConfigureAwait(false); } if (workspace is null) - (workspace, solution) = await lspWorkspaceManager.GetLspSolutionInfoAsync(requestCancellationToken).ConfigureAwait(false); + { + (workspace, solution) = await lspWorkspaceManager.GetLspSolutionInfoAsync(cancellationToken).ConfigureAwait(false); + } if (workspace is null) { - logger.TraceError("Could not find appropriate workspace for operation"); - return null; + logger.LogError($"Could not find appropriate workspace for operation {workspace} on {method}"); } - var context = new RequestContext( + context = new RequestContext( workspace, solution, - document, logger, + method, clientCapabilities, serverKind, + document, documentChangeTracker, trackedDocuments, supportedLanguages, lspServices, - queueCancellationToken); - return context; + cancellationToken); } - /// - /// Allows a mutating request to open a document and start it being tracked. - /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. - /// - public void StartTracking(Uri uri, SourceText initialText) - => _documentChangeTracker.StartTracking(uri, initialText); - - /// - /// Allows a mutating request to update the contents of a tracked document. - /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. - /// - public void UpdateTrackedDocument(Uri uri, SourceText changedText) - => _documentChangeTracker.UpdateTrackedDocument(uri, changedText); - - public SourceText GetTrackedDocumentSourceText(Uri documentUri) - { - Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(documentUri), $"Attempted to get text for {documentUri} which is not open."); - return _trackedDocuments[documentUri]; - } + return context; + } - /// - /// Allows a mutating request to close a document and stop it being tracked. - /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. - /// - public void StopTracking(Uri uri) - => _documentChangeTracker.StopTracking(uri); + /// + /// Allows a mutating request to open a document and start it being tracked. + /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. + /// + public void StartTracking(Uri uri, SourceText initialText) + => _documentChangeTracker.StartTracking(uri, initialText); - public bool IsTracking(Uri documentUri) - => _trackedDocuments.ContainsKey(documentUri); + /// + /// Allows a mutating request to update the contents of a tracked document. + /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. + /// + public void UpdateTrackedDocument(Uri uri, SourceText changedText) + => _documentChangeTracker.UpdateTrackedDocument(uri, changedText); - /// - /// Logs an informational message. - /// - public void TraceInformation(string message) - => _logger.TraceInformation(message); + public SourceText GetTrackedDocumentSourceText(Uri documentUri) + { + Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(documentUri), $"Attempted to get text for {documentUri} which is not open."); + return _trackedDocuments[documentUri]; + } - public void TraceWarning(string message) - => _logger.TraceWarning(message); + /// + /// Allows a mutating request to close a document and stop it being tracked. + /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. + /// + public void StopTracking(Uri uri) + => _documentChangeTracker.StopTracking(uri); - public void TraceError(string message) - => _logger.TraceError(message); + public bool IsTracking(Uri documentUri) + => _trackedDocuments.ContainsKey(documentUri); - public void TraceException(Exception exception) - => _logger.TraceException(exception); + /// + /// Logs an informational message. + /// + public void TraceInformation(string message) + => _logger.LogInformation(message); - public T GetRequiredLspService() where T : class, ILspService - { - return _lspServices.GetRequiredService(); - } + public void TraceWarning(string message) + => _logger.LogWarning(message); + + public void TraceError(string message) + => _logger.LogError(message); + + public void TraceException(Exception exception) + => _logger.LogException(exception); + + public T GetRequiredLspService() where T : class, ILspService + { + return _lspServices.GetRequiredService(); + } + + public T GetRequiredService() where T : class + { + return _lspServices.GetRequiredService(); + } + + public IEnumerable GetRequiredServices() where T : class + { + return _lspServices.GetRequiredServices(); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestContextFactory.cs b/src/Features/LanguageServer/Protocol/Handler/RequestContextFactory.cs new file mode 100644 index 0000000000000..442250e7b2e2d --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/RequestContextFactory.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; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +internal class RequestContextFactory : IRequestContextFactory, ILspService +{ + private readonly ILspServices _lspServices; + + public RequestContextFactory(ILspServices lspServices) + { + _lspServices = lspServices; + } + + public Task CreateRequestContextAsync(IQueueItem queueItem, TRequestParam requestParam, CancellationToken cancellationToken) + { + var clientCapabilitiesManager = _lspServices.GetRequiredService(); + var clientCapabilities = clientCapabilitiesManager.TryGetClientCapabilities(); + var logger = _lspServices.GetRequiredService(); + var serverInfoProvider = _lspServices.GetRequiredService(); + + if (clientCapabilities is null && queueItem.MethodName != Methods.InitializeName) + { + throw new InvalidOperationException($"ClientCapabilities was null for a request other than {Methods.InitializeName}."); + } + + TextDocumentIdentifier? textDocumentIdentifier; + var textDocumentIdentifierHandler = queueItem.MethodHandler as ITextDocumentIdentifierHandler; + if (textDocumentIdentifierHandler is ITextDocumentIdentifierHandler tHandler) + { + textDocumentIdentifier = tHandler.GetTextDocumentIdentifier(requestParam); + } + else if (textDocumentIdentifierHandler is ITextDocumentIdentifierHandler nullHandler) + { + textDocumentIdentifier = nullHandler.GetTextDocumentIdentifier(requestParam); + } + else if (textDocumentIdentifierHandler is ITextDocumentIdentifierHandler uHandler) + { + var uri = uHandler.GetTextDocumentIdentifier(requestParam); + textDocumentIdentifier = new TextDocumentIdentifier + { + Uri = uri, + }; + } + else if (textDocumentIdentifierHandler is null) + { + textDocumentIdentifier = null; + } + else + { + throw new NotImplementedException($"TextDocumentIdentifier in an unrecognized type for method: {queueItem.MethodName}"); + } + + bool requiresLSPSolution; + if (queueItem.MethodHandler is ISolutionRequiredHandler requiredHandler) + { + requiresLSPSolution = requiredHandler.RequiresLSPSolution; + } + else + { + throw new InvalidOperationException($"{nameof(IMethodHandler)} implementation {queueItem.MethodHandler.GetType()} does not implement {nameof(ISolutionRequiredHandler)}"); + } + + return RequestContext.CreateAsync( + queueItem.MutatesServerState, + requiresLSPSolution, + textDocumentIdentifier, + serverInfoProvider.ServerKind, + clientCapabilities, + serverInfoProvider.SupportedLanguages, + _lspServices, + logger, + queueItem.MethodName, + cancellationToken); + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.QueueItem.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.QueueItem.cs deleted file mode 100644 index e08fc12b784d6..0000000000000 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.QueueItem.cs +++ /dev/null @@ -1,199 +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. - -#nullable enable - -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Microsoft.VisualStudio.Threading; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler -{ - internal partial class RequestExecutionQueue - { - private interface IQueueItem - { - /// - /// Begins executing the work specified by this queue item. - /// - Task CallbackAsync(RequestContext? context, CancellationToken cancellationToken); - - /// - bool RequiresLSPSolution { get; } - - /// - bool MutatesSolutionState { get; } - - string MethodName { get; } - - /// - /// The document identifier that will be used to find the solution and document for this request. This comes from the returned from the handler itself via a call to . - /// - TextDocumentIdentifier? TextDocument { get; } - - /// - ClientCapabilities ClientCapabilities { get; } - - /// - /// used to properly correlate this work with the loghub - /// tracing/logging subsystem. - /// - Guid ActivityId { get; } - - RequestMetrics Metrics { get; } - } - - private class QueueItem : IQueueItem - { - private readonly ILspLogger _logger; - - private readonly TRequestType _request; - private readonly IRequestHandler _handler; - - /// - /// A task completion source representing the result of this queue item's work. - /// This is the task that the client is waiting on. - /// - private readonly TaskCompletionSource _completionSource = new(); - - public bool RequiresLSPSolution { get; } - - public bool MutatesSolutionState { get; } - - public string MethodName { get; } - - public TextDocumentIdentifier? TextDocument { get; } - - public ClientCapabilities ClientCapabilities { get; } - - public Guid ActivityId { get; } - - public RequestMetrics Metrics { get; } - - public QueueItem( - bool mutatesSolutionState, - bool requiresLSPSolution, - ClientCapabilities clientCapabilities, - string methodName, - TextDocumentIdentifier? textDocument, - TRequestType request, - IRequestHandler handler, - Guid activityId, - ILspLogger logger, - RequestTelemetryLogger telemetryLogger, - CancellationToken cancellationToken) - { - // Set the tcs state to cancelled if the token gets cancelled outside of our callback (for example the server shutting down). - cancellationToken.Register(() => _completionSource.TrySetCanceled(cancellationToken)); - - Metrics = new RequestMetrics(methodName, telemetryLogger); - - _handler = handler; - _logger = logger; - _request = request; - - ActivityId = activityId; - MutatesSolutionState = mutatesSolutionState; - RequiresLSPSolution = requiresLSPSolution; - ClientCapabilities = clientCapabilities; - MethodName = methodName; - TextDocument = textDocument; - } - - public static (IQueueItem, Task) Create( - bool mutatesSolutionState, - bool requiresLSPSolution, - ClientCapabilities clientCapabilities, - string methodName, - TextDocumentIdentifier? textDocument, - TRequestType request, - IRequestHandler handler, - Guid activityId, - ILspLogger logger, - LspServices lspServices, - CancellationToken cancellationToken) - { - var queueItem = new QueueItem( - mutatesSolutionState, - requiresLSPSolution, - clientCapabilities, - methodName, - textDocument, - request, - handler, - activityId, - logger, - lspServices.GetRequiredService(), - cancellationToken); - - return (queueItem, queueItem._completionSource.Task); - } - - /// - /// Processes the queued request. Exceptions will be sent to the task completion source - /// representing the task that the client is waiting for, then re-thrown so that - /// the queue can correctly handle them depending on the type of request. - /// - public async Task CallbackAsync(RequestContext? context, CancellationToken cancellationToken) - { - // Restore our activity id so that logging/tracking works. - Trace.CorrelationManager.ActivityId = ActivityId; - _logger.TraceStart($"{MethodName} - Roslyn"); - try - { - cancellationToken.ThrowIfCancellationRequested(); - - TResponseType? result; - if (context == null) - { - // If we weren't able to get a corresponding context for this request (for example, we - // couldn't map a doc request to a particular Document, or we couldn't find an appropriate - // Workspace for a global operation), then just immediately complete the request with a - // 'null' response. Note: the lsp spec was checked to ensure that 'null' is valid for all - // the requests this could happen for. However, this assumption may not hold in the future. - // If that turns out to be the case, we could defer to the individual handler to decide - // what to do. - _logger.TraceWarning($"Could not get request context for {MethodName}"); - this.Metrics.RecordFailure(); - result = default; - } - else - { - result = await _handler.HandleRequestAsync(_request, context.Value, cancellationToken).ConfigureAwait(false); - this.Metrics.RecordSuccess(); - } - - _completionSource.TrySetResult(result); - } - catch (OperationCanceledException ex) - { - // Record logs + metrics on cancellation. - _logger.TraceInformation($"{MethodName} - Canceled"); - this.Metrics.RecordCancellation(); - - _completionSource.TrySetCanceled(ex.CancellationToken); - } - catch (Exception ex) - { - // Record logs and metrics on the exception. - _logger.TraceException(ex); - this.Metrics.RecordFailure(); - - _completionSource.TrySetException(ex); - } - finally - { - _logger.TraceStop($"{MethodName} - Roslyn"); - } - - // Return the result of this completion source to the caller - // so it can decide how to handle the result / exception. - await _completionSource.Task.ConfigureAwait(false); - } - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestMetrics.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestMetrics.cs deleted file mode 100644 index b801787b4e1b3..0000000000000 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestMetrics.cs +++ /dev/null @@ -1,56 +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 Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler -{ - internal partial class RequestExecutionQueue - { - internal class RequestMetrics - { - private readonly string _methodName; - private readonly SharedStopwatch _sharedStopWatch; - private TimeSpan? _queuedDuration; - - private readonly RequestTelemetryLogger _requestTelemetryLogger; - - public RequestMetrics(string methodName, RequestTelemetryLogger requestTelemetryLogger) - { - _methodName = methodName; - _requestTelemetryLogger = requestTelemetryLogger; - _sharedStopWatch = SharedStopwatch.StartNew(); - } - - public void RecordExecutionStart() - { - // Request has de-queued and is starting execution. Record the time it spent in queue. - _queuedDuration = _sharedStopWatch.Elapsed; - } - - public void RecordSuccess() - { - RecordCompletion(RequestTelemetryLogger.Result.Succeeded); - } - - public void RecordFailure() - { - RecordCompletion(RequestTelemetryLogger.Result.Failed); - } - - public void RecordCancellation() - { - RecordCompletion(RequestTelemetryLogger.Result.Cancelled); - } - - private void RecordCompletion(RequestTelemetryLogger.Result result) - { - Contract.ThrowIfNull(_queuedDuration, "RecordExecutionStart was not called"); - var overallDuration = _sharedStopWatch.Elapsed; - _requestTelemetryLogger.UpdateTelemetryData(_methodName, _queuedDuration.Value, overallDuration, result); - } - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs deleted file mode 100644 index 02fc627e26031..0000000000000 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs +++ /dev/null @@ -1,303 +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.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler -{ - /// - /// Coordinates the exectution of LSP messages to ensure correct results are sent back. - /// - /// - /// - /// When a request comes in for some data the handler must be able to access a solution state that is correct - /// at the time of the request, that takes into account any text change requests that have come in previously - /// (via textDocument/didChange for example). - /// - /// - /// This class acheives this by distinguishing between mutating and non-mutating requests, and ensuring that - /// when a mutating request comes in, its processing blocks all subsequent requests. As each request comes in - /// it is added to a queue, and a queue item will not be retrieved while a mutating request is running. Before - /// any request is handled the solution state is created by merging workspace solution state, which could have - /// changes from non-LSP means (eg, adding a project reference), with the current "mutated" state. - /// When a non-mutating work item is retrieved from the queue, it is given the current solution state, but then - /// run in a fire-and-forget fashion. - /// - /// - /// Regardless of whether a request is mutating or not, or blocking or not, is an implementation detail of this class - /// and any consumers observing the results of the task returned from - /// will see the results of the handling of the request, whenever it occurred. - /// - /// - /// Exceptions in the handling of non-mutating requests are sent back to callers. Exceptions in the processing of - /// the queue will close the LSP connection so that the client can reconnect. Exceptions in the handling of mutating - /// requests will also close the LSP connection, as at that point the mutated solution is in an unknown state. - /// - /// - /// After shutdown is called, or an error causes the closing of the connection, the queue will not accept any - /// more messages, and a new queue will need to be created. - /// - /// - internal partial class RequestExecutionQueue - { - private readonly WellKnownLspServerKinds _serverKind; - private readonly ImmutableArray _supportedLanguages; - private readonly ILspLogger _logger; - private readonly LspServices _lspServices; - - /// - /// The queue containing the ordered LSP requests along with a combined cancellation token - /// representing the queue's cancellation token and the individual request cancellation token. - /// - private readonly AsyncQueue<(IQueueItem queueItem, CancellationToken cancellationToken)> _queue = new(); - private readonly CancellationTokenSource _cancelSource = new(); - - /// - /// For test purposes only. - /// A task that completes when the queue processing stops. - /// - private readonly Task _queueProcessingTask; - - public CancellationToken CancellationToken => _cancelSource.Token; - - /// - /// Raised when the execution queue has failed, or the solution state its tracking is in an unknown state - /// and so the only course of action is to shutdown the server so that the client re-connects and we can - /// start over again. - /// - /// - /// Once this event has been fired all currently active and pending work items in the queue will be cancelled. - /// - public event EventHandler? RequestServerShutdown; - - public RequestExecutionQueue( - ImmutableArray supportedLanguages, - WellKnownLspServerKinds serverKind, - LspServices services) - { - _supportedLanguages = supportedLanguages; - _serverKind = serverKind; - _lspServices = services; - _logger = _lspServices.GetRequiredService(); - - // Start the queue processing - _queueProcessingTask = ProcessQueueAsync(); - } - - /// - /// Shuts down the queue, stops accepting new messages, and cancels any in-progress or queued tasks. Calling - /// this multiple times won't cause any issues. - /// - public void Shutdown() - { - _cancelSource.Cancel(); - - // Tell the queue not to accept any more items. - // Note: We do not need to spin through the queue manually and cancel items as - // 1. New queue instances are created for each server, so items in the queue would be gc'd. - // 2. Their cancellation tokens are linked to the queue's _cancelSource so are also cancelled. - _queue.Complete(); - } - - /// - /// Queues a request to be handled by the specified handler, with mutating requests blocking subsequent requests - /// from starting until the mutation is complete. - /// - /// Whether or not handling this method results in changes to the current solution state. - /// Mutating requests will block all subsequent requests from starting until after they have - /// completed and mutations have been applied. - /// Whether or not to build a solution that represents the LSP view of the world. If this - /// is set to false, the default workspace's current solution will be used. - /// The handler that will handle the request. - /// The request to handle. - /// The client capabilities. - /// The name of the LSP method. - /// A cancellation token that will cancel the handing of this request. - /// The request could also be cancelled by the queue shutting down. - /// A task that can be awaited to observe the results of the handing of this request. - public Task ExecuteAsync( - bool mutatesSolutionState, - bool requiresLSPSolution, - IRequestHandler handler, - TRequestType request, - ClientCapabilities clientCapabilities, - string methodName, - CancellationToken requestCancellationToken) - where TRequestType : class - { - // Note: If the queue is not accepting any more items then TryEnqueue below will fail. - - var textDocument = handler.GetTextDocumentIdentifier(request); - - // Create a combined cancellation token so either the client cancelling it's token or the queue - // shutting down cancels the request. - var combinedTokenSource = _cancelSource.Token.CombineWith(requestCancellationToken); - var combinedCancellationToken = combinedTokenSource.Token; - - var (item, resultTask) = QueueItem.Create( - mutatesSolutionState, - requiresLSPSolution, - clientCapabilities, - methodName, - textDocument, - request, - handler, - Trace.CorrelationManager.ActivityId, - _logger, - _lspServices, - combinedCancellationToken); - - // Run a continuation to ensure the cts is disposed of. - // We pass CancellationToken.None as we always want to dispose of the source - // even when the request is cancelled or the queue is shutting down. - _ = resultTask.ContinueWith(_ => combinedTokenSource.Dispose(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - - var didEnqueue = _queue.TryEnqueue((item, combinedCancellationToken)); - - // If the queue has been shut down the enqueue will fail, so we just fault the task immediately. - // The queue itself is threadsafe (_queue.TryEnqueue and _queue.Complete use the same lock). - if (!didEnqueue) - { - return Task.FromException(new InvalidOperationException($"{_serverKind.ToUserVisibleString()} was requested to shut down.")); - } - - return resultTask; - } - - private async Task ProcessQueueAsync() - { - try - { - while (!_cancelSource.IsCancellationRequested) - { - // First attempt to de-queue the work item in its own try-catch. - // This is because before we de-queue we do not have access to the queue item's linked cancellation token. - (IQueueItem work, CancellationToken cancellationToken) queueItem; - try - { - queueItem = await _queue.DequeueAsync(_cancelSource.Token).ConfigureAwait(false); - } - catch (OperationCanceledException ex) when (ex.CancellationToken == _cancelSource.Token) - { - // The queue's cancellation token was invoked which means we are shutting down the queue. - // Exit out of the loop so we stop processing new items. - return; - } - - try - { - var (work, cancellationToken) = queueItem; - // Record when the work item was been de-queued and the request context preparation started. - work.Metrics.RecordExecutionStart(); - - // Restore our activity id so that logging/tracking works across asynchronous calls. - Trace.CorrelationManager.ActivityId = work.ActivityId; - var context = await CreateRequestContextAsync(work, cancellationToken).ConfigureAwait(false); - - if (work.MutatesSolutionState) - { - // Mutating requests block other requests from starting to ensure an up to date snapshot is used. - // Since we're explicitly awaiting exceptions to mutating requests will bubble up here. - await work.CallbackAsync(context, cancellationToken).ConfigureAwait(false); - } - else - { - // Non mutating are fire-and-forget because they are by definition readonly. Any errors - // will be sent back to the client but we can still capture errors in queue processing - // via NFW, though these errors don't put us into a bad state as far as the rest of the queue goes. - // Furthermore we use Task.Run here to protect ourselves against synchronous execution of work - // blocking the request queue for longer periods of time (it enforces parallelizabilty). - _ = Task.Run(() => work.CallbackAsync(context, cancellationToken), cancellationToken).ReportNonFatalErrorAsync(); - } - } - catch (OperationCanceledException ex) when (ex.CancellationToken == queueItem.cancellationToken) - { - // Explicitly ignore this exception as cancellation occured as a result of our linked cancellation token. - // This means either the queue is shutting down or the request itself was cancelled. - // 1. If the queue is shutting down, then while loop will exit before the next iteration since it checks for cancellation. - // 2. Request cancellations are normal so no need to report anything there. - } - } - } - catch (Exception ex) when (FatalError.ReportAndCatch(ex)) - { - // We encountered an unexpected exception in processing the queue or in a mutating request. - // Log it, shutdown the queue, and exit the loop. - _logger.TraceException(ex); - OnRequestServerShutdown($"Error occurred processing queue in {_serverKind.ToUserVisibleString()}: {ex.Message}."); - return; - } - } - - private void OnRequestServerShutdown(string message) - { - RequestServerShutdown?.Invoke(this, new RequestShutdownEventArgs(message)); - - Shutdown(); - } - - private Task CreateRequestContextAsync(IQueueItem queueItem, CancellationToken cancellationToken) - { - return RequestContext.CreateAsync( - queueItem.RequiresLSPSolution, - queueItem.MutatesSolutionState, - queueItem.TextDocument, - _serverKind, - queueItem.ClientCapabilities, - _supportedLanguages, - _lspServices, - queueCancellationToken: this.CancellationToken, - requestCancellationToken: cancellationToken); - } - - #region Test Accessor - internal TestAccessor GetTestAccessor() - => new(this); - - internal readonly struct TestAccessor - { - private readonly RequestExecutionQueue _queue; - - public TestAccessor(RequestExecutionQueue queue) - => _queue = queue; - - public bool IsComplete() => _queue._queue.IsCompleted && _queue._queue.IsEmpty; - - public async Task WaitForProcessingToStopAsync() - { - await _queue._queueProcessingTask.ConfigureAwait(false); - } - - /// - /// Test only method to validate that remaining items in the queue are cancelled. - /// This directly mutates the queue in an unsafe way, so ensure that all relevant queue operations - /// are done before calling. - /// - public async Task AreAllItemsCancelledUnsafeAsync() - { - while (!_queue._queue.IsEmpty) - { - var (_, cancellationToken) = await _queue._queue.DequeueAsync().ConfigureAwait(false); - if (!cancellationToken.IsCancellationRequested) - { - return false; - } - } - - return true; - } - } - - #endregion - } -} diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestMetrics.cs b/src/Features/LanguageServer/Protocol/Handler/RequestMetrics.cs new file mode 100644 index 0000000000000..e2c01bccc5cc4 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/RequestMetrics.cs @@ -0,0 +1,54 @@ +// 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 Microsoft.CommonLanguageServerProtocol.Framework; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler +{ + internal class RequestMetrics + { + private readonly string _methodName; + private readonly SharedStopwatch _sharedStopWatch; + private TimeSpan? _queuedDuration; + + private readonly RequestTelemetryLogger _requestTelemetryLogger; + + public RequestMetrics(string methodName, RequestTelemetryLogger requestTelemetryLogger) + { + _methodName = methodName; + _requestTelemetryLogger = requestTelemetryLogger; + _sharedStopWatch = SharedStopwatch.StartNew(); + } + + public void RecordExecutionStart() + { + // Request has de-queued and is starting execution. Record the time it spent in queue. + _queuedDuration = _sharedStopWatch.Elapsed; + } + + public void RecordSuccess() + { + RecordCompletion(RequestTelemetryLogger.Result.Succeeded); + } + + public void RecordFailure() + { + RecordCompletion(RequestTelemetryLogger.Result.Failed); + } + + public void RecordCancellation() + { + RecordCompletion(RequestTelemetryLogger.Result.Cancelled); + } + + private void RecordCompletion(RequestTelemetryLogger.Result result) + { + Contract.ThrowIfNull(_queuedDuration, "RecordExecutionStart was not called"); + var overallDuration = _sharedStopWatch.Elapsed; + _requestTelemetryLogger.UpdateTelemetryData(_methodName, _queuedDuration.Value, overallDuration, result); + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestShutdownEventArgs.cs b/src/Features/LanguageServer/Protocol/Handler/RequestShutdownEventArgs.cs deleted file mode 100644 index 82638d47d5726..0000000000000 --- a/src/Features/LanguageServer/Protocol/Handler/RequestShutdownEventArgs.cs +++ /dev/null @@ -1,18 +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; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler -{ - internal class RequestShutdownEventArgs : EventArgs - { - public string Message { get; } - - public RequestShutdownEventArgs(string message) - { - this.Message = message; - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index 2b5a5e8a20b09..0f50523e03e6c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -2,22 +2,12 @@ // 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.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Api; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -25,139 +15,39 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens { [Method(Methods.TextDocumentSemanticTokensRangeName)] - internal class SemanticTokensRangeHandler : IRequestHandler, IDisposable + internal class SemanticTokensRangeHandler : ILspServiceDocumentRequestHandler { private readonly IGlobalOptionService _globalOptions; - private readonly IAsynchronousOperationListener _asyncListener; - - private readonly CancellationTokenSource _disposalTokenSource; + private readonly SemanticTokensRefreshQueue _semanticTokenRefreshQueue; public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - #region Semantic Tokens Refresh state - - /// - /// Lock over the mutable state that follows. - /// - private readonly object _gate = new(); - - /// - /// Mapping from project id to the workqueue for producing the corresponding compilation for it on the OOP server. - /// - private readonly Dictionary _projectIdToEventSource = new(); - - /// - /// Mapping from project id to the project-cone-checksum for it we were at when the project for it had its - /// compilation produced on the oop server. - /// - private readonly Dictionary _projectIdToLastComputedChecksum = new(); - - private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; - - /// - /// Debouncing queue so that we don't attempt to issue a semantic tokens refresh notification too often. - /// - /// Null when the client does not support sending refresh notifications. - /// - private readonly AsyncBatchingWorkQueue? _semanticTokenRefreshQueue; - - #endregion - public SemanticTokensRangeHandler( IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, - LspWorkspaceRegistrationService lspWorkspaceRegistrationService, - LspWorkspaceManager lspWorkspaceManager, - ILanguageServerNotificationManager notificationManager, - ClientCapabilities clientCapabilities) + SemanticTokensRefreshQueue semanticTokensRefreshQueue) { _globalOptions = globalOptions; - _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Classification); - - _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; - _disposalTokenSource = new(); - - if (clientCapabilities.Workspace?.SemanticTokens?.RefreshSupport is true) - { - // Only send a refresh notification to the client every 2s (if needed) in order to avoid - // sending too many notifications at once. This ensures we batch up workspace notifications, - // but also means we send soon enough after a compilation-computation to not make the user wait - // an enormous amount of time. - _semanticTokenRefreshQueue = new AsyncBatchingWorkQueue( - delay: TimeSpan.FromMilliseconds(2000), - processBatchAsync: (documentUris, cancellationToken) - => FilterLspTrackedDocumentsAsync(lspWorkspaceManager, notificationManager, documentUris, cancellationToken), - equalityComparer: EqualityComparer.Default, - asyncListener: _asyncListener, - _disposalTokenSource.Token); - - _lspWorkspaceRegistrationService.LspSolutionChanged += OnLspSolutionChanged; - } + _semanticTokenRefreshQueue = semanticTokensRefreshQueue; } - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.SemanticTokensRangeParams request) + public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.SemanticTokensRangeParams request) { Contract.ThrowIfNull(request.TextDocument); return request.TextDocument; } - private static ValueTask FilterLspTrackedDocumentsAsync( - LspWorkspaceManager lspWorkspaceManager, - ILanguageServerNotificationManager notificationManager, - ImmutableSegmentedList documentUris, - CancellationToken cancellationToken) - { - var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); - foreach (var documentUri in documentUris) - { - if (documentUri is null || !trackedDocuments.ContainsKey(documentUri)) - { - return notificationManager.SendNotificationAsync(Methods.WorkspaceSemanticTokensRefreshName, cancellationToken); - } - } - - // LSP is already tracking all changed documents so we don't need to send a refresh request. - return ValueTaskFactory.CompletedTask; - } - - private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) - { - if (e.DocumentId is not null && e.Kind is WorkspaceChangeKind.DocumentChanged) - { - var document = e.NewSolution.GetRequiredDocument(e.DocumentId); - var documentUri = document.GetURI(); - - // We enqueue the URI since there's a chance the client is already tracking the - // document, in which case we don't need to send a refresh notification. - // We perform the actual check when processing the batch to ensure we have the - // most up-to-date list of tracked documents. - EnqueueSemanticTokenRefreshNotification(documentUri); - } - else - { - EnqueueSemanticTokenRefreshNotification(documentUri: null); - } - } - - private void EnqueueSemanticTokenRefreshNotification(Uri? documentUri) - { - // We should have only gotten here if semantic tokens refresh is supported. - Contract.ThrowIfNull(_semanticTokenRefreshQueue); - _semanticTokenRefreshQueue.AddWork(documentUri); - } - public async Task HandleRequestAsync( SemanticTokensRangeParams request, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(request.TextDocument, "TextDocument is null."); - Contract.ThrowIfNull(context.Document, "Document is null."); + var contextDocument = context.GetRequiredDocument(); // If the full compilation is not yet available, we'll try getting a partial one. It may contain inaccurate // results but will speed up how quickly we can respond to the client's request. - var document = context.Document.WithFrozenPartialSemantics(cancellationToken); + var document = contextDocument.WithFrozenPartialSemantics(cancellationToken); var project = document.Project; var options = _globalOptions.GetClassificationOptions(project.Language) with { ForceFrozenPartialSemanticsForCrossProcessOperations = true }; @@ -170,84 +60,16 @@ private void EnqueueSemanticTokenRefreshNotification(Uri? documentUri) SemanticTokensHelpers.TokenTypeToIndex, request.Range, options, - includeSyntacticClassifications: context.Document.IsRazorDocument(), + includeSyntacticClassifications: contextDocument.IsRazorDocument(), cancellationToken).ConfigureAwait(false); // The above call to get semantic tokens may be inaccurate (because we use frozen partial semantics). Kick // off a request to ensure that the OOP side gets a fully up to compilation for this project. Once it does // we can optionally choose to notify our caller to do a refresh if we computed a compilation for a new // solution snapshot. - if (_semanticTokenRefreshQueue != null) - await TryEnqueueRefreshComputationAsync(project, cancellationToken).ConfigureAwait(false); + await _semanticTokenRefreshQueue.TryEnqueueRefreshComputationAsync(project, cancellationToken).ConfigureAwait(false); return new LSP.SemanticTokens { Data = tokensData }; } - - private async Task TryEnqueueRefreshComputationAsync(Project project, CancellationToken cancellationToken) - { - // Determine the checksum for this project cone. Note: this should be fast in practice because this is - // the same project-cone-checksum we used to even call into OOP above when we computed semantic tokens. - var projectChecksum = await project.Solution.State.GetChecksumAsync(project.Id, cancellationToken).ConfigureAwait(false); - - lock (_gate) - { - // If this checksum is the same as the last computed result, no need to continue, we would not produce a - // different compilation. - if (ChecksumIsUnchanged_NoLock(project, projectChecksum)) - return; - - if (!_projectIdToEventSource.TryGetValue(project.Id, out var eventSource)) - { - eventSource = new CompilationAvailableEventSource(_asyncListener); - _projectIdToEventSource.Add(project.Id, eventSource); - } - - eventSource.EnsureCompilationAvailability(project, () => OnCompilationAvailable(project, projectChecksum)); - } - } - - private void OnCompilationAvailable(Project project, Checksum projectChecksum) - { - lock (_gate) - { - // Paranoia: It's technically possible (though unlikely) for two calls to compute the compilation for - // the same project to come back and call into this. This is because the - // CompilationAvailableEventSource uses cooperative cancellation to cancel the in-flight request before - // issuing the new one. There is no requirement though that the inflight request actually stop (or run - // to completion) prior to the next request running and completing. In practice this should not happen - // as cancellation is checked fairly regularly. However, if it does, check and do not bother to issue a - // refresh in this case. - if (ChecksumIsUnchanged_NoLock(project, projectChecksum)) - return; - - // keep track of this checksum. That way we don't get into a loop where we send a refresh notification, - // then we get called back into, causing us to compute the compilation, causing us to send the refresh - // notification, etc. etc. - _projectIdToLastComputedChecksum[project.Id] = projectChecksum; - } - - EnqueueSemanticTokenRefreshNotification(documentUri: null); - } - - private bool ChecksumIsUnchanged_NoLock(Project project, Checksum projectChecksum) - => _projectIdToLastComputedChecksum.TryGetValue(project.Id, out var lastChecksum) && lastChecksum == projectChecksum; - - public void Dispose() - { - ImmutableArray eventSources; - lock (_gate) - { - eventSources = _projectIdToEventSource.Values.ToImmutableArray(); - _projectIdToEventSource.Clear(); - - _lspWorkspaceRegistrationService.LspSolutionChanged -= OnLspSolutionChanged; - } - - foreach (var eventSource in eventSources) - eventSource.Dispose(); - - _disposalTokenSource.Cancel(); - _disposalTokenSource.Dispose(); - } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandlerFactory.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandlerFactory.cs index 21e83c5f65a15..d01038e3d890c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandlerFactory.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandlerFactory.cs @@ -6,7 +6,6 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens { @@ -14,27 +13,19 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens internal sealed class SemanticTokensRangeHandlerFactory : ILspServiceFactory { private readonly IGlobalOptionService _globalOptions; - private readonly IAsynchronousOperationListenerProvider _asyncListenerProvider; - private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public SemanticTokensRangeHandlerFactory( - IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, - LspWorkspaceRegistrationService lspWorkspaceRegistrationService) + IGlobalOptionService globalOptions) { _globalOptions = globalOptions; - _asyncListenerProvider = asynchronousOperationListenerProvider; - _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; } public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { - var clientCapabilities = lspServices.GetRequiredService().GetClientCapabilities(); - var notificationManager = lspServices.GetRequiredService(); - var lspWorkspaceManager = lspServices.GetRequiredService(); - return new SemanticTokensRangeHandler(_globalOptions, _asyncListenerProvider, _lspWorkspaceRegistrationService, lspWorkspaceManager, notificationManager, clientCapabilities); + var semanticTokensRefreshQueue = lspServices.GetRequiredService(); + return new SemanticTokensRangeHandler(_globalOptions, semanticTokensRefreshQueue); } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs new file mode 100644 index 0000000000000..2e9d23c2f9e0c --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs @@ -0,0 +1,211 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; + +/// +/// Batches requests to refresh the semantic tokens to optomize user experience. +/// +/// This implements to avoid race conditions +/// related to creating the queue on the first request. +internal class SemanticTokensRefreshQueue : + IOnInitialized, + ILspService, + IDisposable +{ + /// + /// Lock over the mutable state that follows. + /// + private readonly object _gate = new(); + + /// + /// Mapping from project id to the workqueue for producing the corresponding compilation for it on the OOP server. + /// + private readonly Dictionary _projectIdToEventSource = new(); + + /// + /// Mapping from project id to the project-cone-checksum for it we were at when the project for it had its + /// compilation produced on the oop server. + /// + private readonly Dictionary _projectIdToLastComputedChecksum = new(); + + private readonly LspWorkspaceManager _lspWorkspaceManager; + private readonly IClientLanguageServerManager _notificationManager; + + private readonly IAsynchronousOperationListener _asyncListener; + private readonly CancellationTokenSource _disposalTokenSource; + + /// + /// Debouncing queue so that we don't attempt to issue a semantic tokens refresh notification too often. + /// + /// Null when the client does not support sending refresh notifications. + /// + private AsyncBatchingWorkQueue? _semanticTokenRefreshQueue; + + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; + + public SemanticTokensRefreshQueue( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService, + LspWorkspaceManager lspWorkspaceManager, + IClientLanguageServerManager notificationManager) + { + _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Classification); + + _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + _disposalTokenSource = new(); + + _lspWorkspaceManager = lspWorkspaceManager; + _notificationManager = notificationManager; + } + + public Task OnInitializedAsync(ClientCapabilities clientCapabilities, CancellationToken _) + { + if (_semanticTokenRefreshQueue is null && clientCapabilities.Workspace?.SemanticTokens?.RefreshSupport is true) + { + // Only send a refresh notification to the client every 2s (if needed) in order to avoid + // sending too many notifications at once. This ensures we batch up workspace notifications, + // but also means we send soon enough after a compilation-computation to not make the user wait + // an enormous amount of time. + _semanticTokenRefreshQueue = new AsyncBatchingWorkQueue( + delay: TimeSpan.FromMilliseconds(2000), + processBatchAsync: (documentUris, cancellationToken) + => FilterLspTrackedDocumentsAsync(_lspWorkspaceManager, _notificationManager, documentUris, cancellationToken), + equalityComparer: EqualityComparer.Default, + asyncListener: _asyncListener, + _disposalTokenSource.Token); + + _lspWorkspaceRegistrationService.LspSolutionChanged += OnLspSolutionChanged; + } + + return Task.CompletedTask; + } + + public async Task TryEnqueueRefreshComputationAsync(Project project, CancellationToken cancellationToken) + { + if (_semanticTokenRefreshQueue is not null) + { + // Determine the checksum for this project cone. Note: this should be fast in practice because this is + // the same project-cone-checksum we used to even call into OOP above when we computed semantic tokens. + var projectChecksum = await project.Solution.State.GetChecksumAsync(project.Id, cancellationToken).ConfigureAwait(false); + + lock (_gate) + { + // If this checksum is the same as the last computed result, no need to continue, we would not produce a + // different compilation. + if (ChecksumIsUnchanged_NoLock(project, projectChecksum)) + return; + + if (!_projectIdToEventSource.TryGetValue(project.Id, out var eventSource)) + { + eventSource = new CompilationAvailableEventSource(_asyncListener); + _projectIdToEventSource.Add(project.Id, eventSource); + } + + eventSource.EnsureCompilationAvailability(project, () => OnCompilationAvailable(project, projectChecksum)); + } + } + } + + private static ValueTask FilterLspTrackedDocumentsAsync( + LspWorkspaceManager lspWorkspaceManager, + IClientLanguageServerManager notificationManager, + ImmutableSegmentedList documentUris, + CancellationToken cancellationToken) + { + var trackedDocuments = lspWorkspaceManager.GetTrackedLspText(); + foreach (var documentUri in documentUris) + { + if (documentUri is null || !trackedDocuments.ContainsKey(documentUri)) + { + return notificationManager.SendNotificationAsync(Methods.WorkspaceSemanticTokensRefreshName, cancellationToken); + } + } + + // LSP is already tracking all changed documents so we don't need to send a refresh request. + return ValueTaskFactory.CompletedTask; + } + + private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) + { + if (e.DocumentId is not null && e.Kind is WorkspaceChangeKind.DocumentChanged) + { + var document = e.NewSolution.GetRequiredDocument(e.DocumentId); + var documentUri = document.GetURI(); + + // We enqueue the URI since there's a chance the client is already tracking the + // document, in which case we don't need to send a refresh notification. + // We perform the actual check when processing the batch to ensure we have the + // most up-to-date list of tracked documents. + EnqueueSemanticTokenRefreshNotification(documentUri); + } + else + { + EnqueueSemanticTokenRefreshNotification(documentUri: null); + } + } + + private void EnqueueSemanticTokenRefreshNotification(Uri? documentUri) + { + // We should have only gotten here if semantic tokens refresh is supported and initialized. + Contract.ThrowIfNull(_semanticTokenRefreshQueue); + _semanticTokenRefreshQueue.AddWork(documentUri); + } + + private void OnCompilationAvailable(Project project, Checksum projectChecksum) + { + lock (_gate) + { + // Paranoia: It's technically possible (though unlikely) for two calls to compute the compilation for + // the same project to come back and call into this. This is because the + // CompilationAvailableEventSource uses cooperative cancellation to cancel the in-flight request before + // issuing the new one. There is no requirement though that the inflight request actually stop (or run + // to completion) prior to the next request running and completing. In practice this should not happen + // as cancellation is checked fairly regularly. However, if it does, check and do not bother to issue a + // refresh in this case. + if (ChecksumIsUnchanged_NoLock(project, projectChecksum)) + return; + + // keep track of this checksum. That way we don't get into a loop where we send a refresh notification, + // then we get called back into, causing us to compute the compilation, causing us to send the refresh + // notification, etc. etc. + _projectIdToLastComputedChecksum[project.Id] = projectChecksum; + } + + EnqueueSemanticTokenRefreshNotification(documentUri: null); + } + + private bool ChecksumIsUnchanged_NoLock(Project project, Checksum projectChecksum) + => _projectIdToLastComputedChecksum.TryGetValue(project.Id, out var lastChecksum) && lastChecksum == projectChecksum; + + public void Dispose() + { + ImmutableArray eventSources; + lock (_gate) + { + eventSources = _projectIdToEventSource.Values.ToImmutableArray(); + _projectIdToEventSource.Clear(); + + _lspWorkspaceRegistrationService.LspSolutionChanged -= OnLspSolutionChanged; + } + + foreach (var eventSource in eventSources) + eventSource.Dispose(); + + _disposalTokenSource.Cancel(); + _disposalTokenSource.Dispose(); + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueueFactory.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueueFactory.cs new file mode 100644 index 0000000000000..803d0a5e0f7d2 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueueFactory.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.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens +{ + [ExportCSharpVisualBasicLspServiceFactory(typeof(SemanticTokensRefreshQueue)), Shared] + internal sealed class SemanticTokensRefreshQueueFactory : ILspServiceFactory + { + private readonly IAsynchronousOperationListenerProvider _asyncListenerProvider; + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public SemanticTokensRefreshQueueFactory( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService) + { + _asyncListenerProvider = asynchronousOperationListenerProvider; + _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + } + + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + var notificationManager = lspServices.GetRequiredService(); + var lspWorkspaceManager = lspServices.GetRequiredService(); + + return new SemanticTokensRefreshQueue(_asyncListenerProvider, _lspWorkspaceRegistrationService, lspWorkspaceManager, notificationManager); + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerInfoProvider.cs b/src/Features/LanguageServer/Protocol/Handler/ServerInfoProvider.cs new file mode 100644 index 0000000000000..f6de6e89adf74 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ServerInfoProvider.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.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler +{ + internal class ServerInfoProvider + { + public ServerInfoProvider(WellKnownLspServerKinds serverKind, ImmutableArray supportedLanguages) + { + ServerKind = serverKind; + SupportedLanguages = supportedLanguages; + } + + public readonly WellKnownLspServerKinds ServerKind; + public readonly ImmutableArray SupportedLanguages; + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs new file mode 100644 index 0000000000000..f1262461726d1 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs @@ -0,0 +1,54 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +[Method(Methods.InitializeName)] +internal class InitializeHandler : ILspServiceRequestHandler +{ + public InitializeHandler() + { + } + + public bool MutatesSolutionState => true; + public bool RequiresLSPSolution => false; + + public Task HandleRequestAsync(InitializeParams request, RequestContext context, CancellationToken cancellationToken) + { + var logger = context.GetRequiredLspService(); + try + { + logger.LogStartContext("Initialize"); + + var clientCapabilitiesManager = context.GetRequiredLspService(); + var clientCapabilities = clientCapabilitiesManager.TryGetClientCapabilities(); + if (clientCapabilities != null) + { + throw new InvalidOperationException($"{nameof(Methods.InitializeName)} called multiple times"); + } + + clientCapabilities = request.Capabilities; + clientCapabilitiesManager.SetClientCapabilities(clientCapabilities); + + var capabilitiesProvider = context.GetRequiredLspService(); + var serverCapabilities = capabilitiesProvider.GetCapabilities(clientCapabilities); + + return Task.FromResult(new InitializeResult + { + Capabilities = serverCapabilities, + }); + } + finally + { + logger.LogEndContext("Initialize"); + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializedHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializedHandler.cs new file mode 100644 index 0000000000000..ffa7325c2405a --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializedHandler.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. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +[Method(Methods.InitializedName)] +internal class InitializedHandler : ILspServiceNotificationHandler +{ + public InitializedHandler() + { + } + + public bool MutatesSolutionState => true; + + public bool RequiresLSPSolution => false; + + public async Task HandleNotificationAsync(InitializedParams request, RequestContext requestContext, CancellationToken cancellationToken) + { + var clientCapabilities = requestContext.GetRequiredClientCapabilities(); + var onInitializeList = requestContext.GetRequiredServices(); + + foreach (var onInitialize in onInitializeList) + { + await onInitialize.OnInitializedAsync(clientCapabilities, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/LspServiceLifeCycleManager.cs b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/LspServiceLifeCycleManager.cs new file mode 100644 index 0000000000000..d288fde036c4a --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/LspServiceLifeCycleManager.cs @@ -0,0 +1,45 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime; + +internal class LspServiceLifeCycleManager : ILifeCycleManager, ILspService +{ + private readonly IClientLanguageServerManager _clientLanguageServerManager; + + public LspServiceLifeCycleManager(IClientLanguageServerManager clientLanguageServerManager) + { + _clientLanguageServerManager = clientLanguageServerManager; + } + + public async Task ShutdownAsync(string message = "Shutting down") + { + try + { + var messageParams = new LogMessageParams() + { + MessageType = MessageType.Info, + Message = message + }; + await _clientLanguageServerManager.SendNotificationAsync("window/logMessage", messageParams, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) + { + //Don't fail shutdown just because jsonrpc has already been cancelled. + } + } + + public Task ExitAsync() + { + // We don't need any custom logic to run on exit. + return Task.CompletedTask; + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index afe0b832bc40a..b49379730822c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(SignatureHelpHandler)), Shared] [Method(LSP.Methods.TextDocumentSignatureHelpName)] - internal class SignatureHelpHandler : IRequestHandler + internal class SignatureHelpHandler : ILspServiceDocumentRequestHandler { private readonly IEnumerable> _allProviders; private readonly IGlobalOptionService _globalOptions; @@ -38,10 +38,11 @@ public SignatureHelpHandler( public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; + public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { + var clientCapabilities = context.GetRequiredClientCapabilities(); var document = context.Document; if (document == null) return null; @@ -63,7 +64,7 @@ public SignatureHelpHandler( foreach (var item in items.Items) { LSP.SignatureInformation sigInfo; - if (context.ClientCapabilities?.HasVisualStudioLspCapability() == true) + if (clientCapabilities.HasVisualStudioLspCapability() == true) { sigInfo = new LSP.VSInternalSignatureInformation { diff --git a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/AbstractSpellCheckingHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/AbstractSpellCheckingHandler.cs index f7f8a29abfb4a..76271b6274e98 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/AbstractSpellCheckingHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/AbstractSpellCheckingHandler.cs @@ -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. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; @@ -11,6 +12,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SpellCheck; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -21,7 +23,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SpellCheck /// Root type for both document and workspace spell checking requests. /// internal abstract class AbstractSpellCheckHandler - : IRequestHandler + : ILspServiceRequestHandler, ITextDocumentIdentifierHandler where TParams : IPartialResultParams where TReport : VSInternalSpellCheckableRangeReport { diff --git a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs index 026432ae26a14..798e420b924e4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Immutable; -using System.Composition; using System.Threading; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SpellCheck @@ -14,7 +12,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SpellCheck [Method(VSInternalMethods.TextDocumentSpellCheckableRangesName)] internal class DocumentSpellCheckHandler : AbstractSpellCheckHandler { - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalDocumentSpellCheckableParams requestParams) + public override TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalDocumentSpellCheckableParams requestParams) => requestParams.TextDocument; protected override VSInternalSpellCheckableRangeReport CreateReport(TextDocumentIdentifier identifier, VSInternalSpellCheckableRange[]? ranges, string? resultId) diff --git a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/WorkspaceSpellCheckHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/WorkspaceSpellCheckHandler.cs index 2686656b13d75..491d9f9211244 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/WorkspaceSpellCheckHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/WorkspaceSpellCheckHandler.cs @@ -2,13 +2,10 @@ // 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.Linq; using System.Threading; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Api; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; @@ -18,9 +15,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SpellCheck [Method(VSInternalMethods.WorkspaceSpellCheckableRangesName)] internal class WorkspaceSpellCheckHandler : AbstractSpellCheckHandler { - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceSpellCheckableParams request) - => null; - protected override VSInternalWorkspaceSpellCheckableReport CreateReport(TextDocumentIdentifier identifier, VSInternalSpellCheckableRange[]? ranges, string? resultId) => new() { @@ -29,6 +23,8 @@ protected override VSInternalWorkspaceSpellCheckableReport CreateReport(TextDocu ResultId = resultId, }; + public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceSpellCheckableParams requestParams) => null; + protected override ImmutableArray? GetPreviousResults(VSInternalWorkspaceSpellCheckableParams requestParams) => requestParams.PreviousResults?.Where(d => d.PreviousResultId != null).Select(d => new PreviousPullResult(d.PreviousResultId!, d.TextDocument!)).ToImmutableArray(); diff --git a/src/Features/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs b/src/Features/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs new file mode 100644 index 0000000000000..6b72be29076fc --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs @@ -0,0 +1,45 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler; + +[ExportCSharpVisualBasicStatelessLspService(typeof(ExecuteWorkspaceCommandHandler)), Shared] +[Method(Methods.WorkspaceExecuteCommandName)] +internal class ExecuteWorkspaceCommandHandler : ILspServiceRequestHandler +{ + public bool MutatesSolutionState => false; + + public bool RequiresLSPSolution => true; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ExecuteWorkspaceCommandHandler() + { + } + + public async Task HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) + { + var requestExecutionQueue = context.GetRequiredService>(); + var lspServices = context.GetRequiredService(); + + var requestMethod = AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(request.Command); + + var result = await requestExecutionQueue.ExecuteAsync( + request, + requestMethod, + lspServices, + cancellationToken).ConfigureAwait(false); + + return result; + } +} diff --git a/src/Features/LanguageServer/Protocol/ICapabilitiesProvider.cs b/src/Features/LanguageServer/Protocol/ICapabilitiesProvider.cs index c1cb95a3c9e8a..317f540badede 100644 --- a/src/Features/LanguageServer/Protocol/ICapabilitiesProvider.cs +++ b/src/Features/LanguageServer/Protocol/ICapabilitiesProvider.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer { - internal interface ICapabilitiesProvider + internal interface ICapabilitiesProvider : ILspService { ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities); } diff --git a/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs b/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs new file mode 100644 index 0000000000000..bb4242b113ba4 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs @@ -0,0 +1,15 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.LanguageServer; + +internal interface IClientLanguageServerManager : ILspService +{ + Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); + ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken); + ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken); +} diff --git a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs index 3edf3d138bba9..374a26457676b 100644 --- a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs @@ -2,15 +2,19 @@ // 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.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer { internal interface ILanguageServerFactory { - public ILanguageServerTarget Create( + public AbstractLanguageServer Create( JsonRpc jsonRpc, ICapabilitiesProvider capabilitiesProvider, - ILspLogger logger); + WellKnownLspServerKinds serverKind, + ILspServiceLogger logger); } } diff --git a/src/Features/LanguageServer/Protocol/ILspLogger.cs b/src/Features/LanguageServer/Protocol/ILspLogger.cs deleted file mode 100644 index f32c32eae22f2..0000000000000 --- a/src/Features/LanguageServer/Protocol/ILspLogger.cs +++ /dev/null @@ -1,32 +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; - -namespace Microsoft.CodeAnalysis.LanguageServer -{ - internal interface ILspLogger : ILspService - { - void TraceInformation(string message); - void TraceWarning(string message); - void TraceError(string message); - void TraceException(Exception exception); - void TraceStart(string message); - void TraceStop(string message); - } - - internal class NoOpLspLogger : ILspLogger - { - public static readonly ILspLogger Instance = new NoOpLspLogger(); - - private NoOpLspLogger() { } - - public void TraceException(Exception exception) { } - public void TraceInformation(string message) { } - public void TraceWarning(string message) { } - public void TraceError(string message) { } - public void TraceStart(string message) { } - public void TraceStop(string message) { } - } -} diff --git a/src/Features/LanguageServer/Protocol/ILanguageServerTarget.cs b/src/Features/LanguageServer/Protocol/ILspServiceLogger.cs similarity index 68% rename from src/Features/LanguageServer/Protocol/ILanguageServerTarget.cs rename to src/Features/LanguageServer/Protocol/ILspServiceLogger.cs index 1aaeefbccdbbf..79a87f31e54ed 100644 --- a/src/Features/LanguageServer/Protocol/ILanguageServerTarget.cs +++ b/src/Features/LanguageServer/Protocol/ILspServiceLogger.cs @@ -2,11 +2,12 @@ // 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 Microsoft.CommonLanguageServerProtocol.Framework; namespace Microsoft.CodeAnalysis.LanguageServer { - internal interface ILanguageServerTarget : IAsyncDisposable + + internal interface ILspServiceLogger : ILspLogger, ILspService { } } diff --git a/src/Features/LanguageServer/Protocol/ILspLoggerFactory.cs b/src/Features/LanguageServer/Protocol/ILspServiceLoggerFactory.cs similarity index 59% rename from src/Features/LanguageServer/Protocol/ILspLoggerFactory.cs rename to src/Features/LanguageServer/Protocol/ILspServiceLoggerFactory.cs index 56a14a6eeebb5..781bb52160fee 100644 --- a/src/Features/LanguageServer/Protocol/ILspLoggerFactory.cs +++ b/src/Features/LanguageServer/Protocol/ILspServiceLoggerFactory.cs @@ -4,12 +4,13 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CommonLanguageServerProtocol.Framework; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer { - internal interface ILspLoggerFactory + internal interface ILspServiceLoggerFactory { - Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken); + Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken); } } diff --git a/src/Features/LanguageServer/Protocol/ILanguageServerNotificationManager.cs b/src/Features/LanguageServer/Protocol/IOnInitialize.cs similarity index 52% rename from src/Features/LanguageServer/Protocol/ILanguageServerNotificationManager.cs rename to src/Features/LanguageServer/Protocol/IOnInitialize.cs index e4c1e030653c3..aca1724edb602 100644 --- a/src/Features/LanguageServer/Protocol/ILanguageServerNotificationManager.cs +++ b/src/Features/LanguageServer/Protocol/IOnInitialize.cs @@ -4,11 +4,11 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer +namespace Microsoft.CodeAnalysis.LanguageServer; + +internal interface IOnInitialized { - internal interface ILanguageServerNotificationManager : ILspService - { - ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken); - } + Task OnInitializedAsync(ClientCapabilities clientCapabilities, CancellationToken cancellationToken); } diff --git a/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs b/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs deleted file mode 100644 index 66815d298e122..0000000000000 --- a/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs +++ /dev/null @@ -1,330 +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.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; -using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Roslyn.Utilities; -using StreamJsonRpc; -using static Microsoft.CodeAnalysis.LanguageServer.Handler.RequestExecutionQueue; -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer -{ - internal class LanguageServerTarget : ILanguageServerTarget, IClientCapabilitiesProvider - { - private readonly ICapabilitiesProvider _capabilitiesProvider; - - private readonly JsonRpc _jsonRpc; - private readonly RequestDispatcher _requestDispatcher; - private readonly RequestExecutionQueue _queue; - private readonly LspServices _lspServices; - private readonly IAsynchronousOperationListener _listener; - private readonly ILspLogger _logger; - - // Set on first LSP initialize request. - private ClientCapabilities? _clientCapabilities; - - // Fields used during shutdown. - private bool _shuttingDown; - private Task? _errorShutdownTask; - - internal bool HasShutdownStarted => _shuttingDown; - - internal LanguageServerTarget( - AbstractLspServiceProvider lspServiceProvider, - JsonRpc jsonRpc, - ICapabilitiesProvider capabilitiesProvider, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLogger logger, - ImmutableArray supportedLanguages, - WellKnownLspServerKinds serverKind) - { - _capabilitiesProvider = capabilitiesProvider; - _logger = logger; - - _jsonRpc = jsonRpc; - _jsonRpc.AddLocalRpcTarget(this); - _jsonRpc.Disconnected += JsonRpc_Disconnected; - - _listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); - - // Add services that require base dependencies (jsonrpc) or are more complex to create to the set manually. - _lspServices = lspServiceProvider.CreateServices(serverKind, ImmutableArray.Create( - CreateLspServiceInstance(new LanguageServerNotificationManager(_jsonRpc)), - CreateLspServiceInstance(logger), - CreateLspServiceInstance(this))); - - _queue = new RequestExecutionQueue( - supportedLanguages, - serverKind, - _lspServices); - _queue.RequestServerShutdown += RequestExecutionQueue_Errored; - - _requestDispatcher = _lspServices.GetRequiredService(); - - var entryPointMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.EntryPointAsync)); - Contract.ThrowIfNull(entryPointMethod, $"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.EntryPointAsync)}"); - - foreach (var metadata in _requestDispatcher.GetRegisteredMethods()) - { - // Instead of concretely defining methods for each LSP method, we instead dynamically construct the - // generic method info from the exported handler types. This allows us to define multiple handlers for - // the same method but different type parameters. This is a key functionality to support TS external - // access as we do not want to couple our LSP protocol version dll to theirs. - // - // We also do not use the StreamJsonRpc support for JToken as the rpc method parameters because we want - // StreamJsonRpc to do the deserialization to handle streaming requests using IProgress. - var delegatingEntryPoint = new DelegatingEntryPoint(metadata.MethodName, this); - - var genericEntryPointMethod = entryPointMethod.MakeGenericMethod(metadata.RequestType, metadata.ResponseType); - - _jsonRpc.AddLocalRpcMethod(genericEntryPointMethod, delegatingEntryPoint, new JsonRpcMethodAttribute(metadata.MethodName) { UseSingleObjectParameterDeserialization = true }); - } - - static Lazy CreateLspServiceInstance(T lspServiceInstance) where T : ILspService - { - return new Lazy( - () => lspServiceInstance, new LspServiceMetadataView(typeof(T))); - } - } - - /// - /// Wrapper class to hold the method and properties from the - /// that the method info passed to streamjsonrpc is created from. - /// - private class DelegatingEntryPoint - { - private readonly string _method; - private readonly LanguageServerTarget _target; - - public DelegatingEntryPoint(string method, LanguageServerTarget target) - { - _method = method; - _target = target; - } - - public async Task EntryPointAsync(TRequestType requestType, CancellationToken cancellationToken) where TRequestType : class - { - Contract.ThrowIfNull(_target._clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - var result = await _target._requestDispatcher.ExecuteRequestAsync( - _method, - requestType, - _target._clientCapabilities, - _target._queue, - cancellationToken).ConfigureAwait(false); - return result; - } - } - - public ClientCapabilities GetClientCapabilities() - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - return _clientCapabilities; - } - - /// - /// Handle the LSP initialize request by storing the client capabilities and responding with the server - /// capabilities. The specification assures that the initialize request is sent only once. - /// - [JsonRpcMethod(Methods.InitializeName, UseSingleObjectParameterDeserialization = true)] - public Task InitializeAsync(InitializeParams initializeParams, CancellationToken cancellationToken) - { - try - { - _logger?.TraceStart("Initialize"); - - Contract.ThrowIfTrue(_clientCapabilities != null, $"{nameof(InitializeAsync)} called multiple times"); - _clientCapabilities = initializeParams.Capabilities; - - return Task.FromResult(new InitializeResult - { - Capabilities = _capabilitiesProvider.GetCapabilities(_clientCapabilities), - }); - } - finally - { - _logger?.TraceStop("Initialize"); - } - } - - [JsonRpcMethod(Methods.InitializedName)] - public virtual Task InitializedAsync(CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities); - return Task.CompletedTask; - } - - [JsonRpcMethod(Methods.ShutdownName)] - public Task ShutdownAsync(CancellationToken _) - { - try - { - _logger?.TraceStart("Shutdown"); - - ShutdownImpl(); - - return Task.CompletedTask; - } - finally - { - _logger?.TraceStop("Shutdown"); - } - } - - private void ShutdownImpl() - { - Contract.ThrowIfTrue(_shuttingDown, "Shutdown has already been called."); - - _shuttingDown = true; - - ShutdownRequestQueue(); - } - - [JsonRpcMethod(Methods.ExitName)] - public Task ExitAsync(CancellationToken _) - { - try - { - _logger?.TraceStart("Exit"); - - ExitImpl(); - - return Task.CompletedTask; - } - finally - { - _logger?.TraceStop("Exit"); - } - } - - private void ExitImpl() - { - try - { - ShutdownRequestQueue(); - - _lspServices.Dispose(); - - _jsonRpc.Disconnected -= JsonRpc_Disconnected; - _jsonRpc.Dispose(); - } - catch (Exception e) when (FatalError.ReportAndCatch(e)) - { - // Swallow exceptions thrown by disposing our JsonRpc object. Disconnected events can potentially throw their own exceptions so - // we purposefully ignore all of those exceptions in an effort to shutdown gracefully. - } - } - - /// - /// Specially handle the execute workspace command method as we have to deserialize the request - /// to figure out which actually handles it. - /// - [JsonRpcMethod(Methods.WorkspaceExecuteCommandName, UseSingleObjectParameterDeserialization = true)] - public async Task ExecuteWorkspaceCommandAsync(LSP.ExecuteCommandParams request, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - var requestMethod = AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(request.Command); - - var result = await _requestDispatcher.ExecuteRequestAsync( - requestMethod, - request, - _clientCapabilities, - _queue, - cancellationToken).ConfigureAwait(false); - return result; - } - - private void ShutdownRequestQueue() - { - _queue.RequestServerShutdown -= RequestExecutionQueue_Errored; - // if the queue requested shutdown via its event, it will have already shut itself down, but this - // won't cause any problems calling it again - _queue?.Shutdown(); - } - - private void RequestExecutionQueue_Errored(object? sender, RequestShutdownEventArgs e) - { - // log message and shut down - _logger?.TraceWarning($"Request queue is requesting shutdown due to error: {e.Message}"); - - var message = new LogMessageParams() - { - MessageType = MessageType.Error, - Message = e.Message - }; - - var asyncToken = _listener.BeginAsyncOperation(nameof(RequestExecutionQueue_Errored)); - _errorShutdownTask = Task.Run(async () => - { - _logger?.TraceInformation("Shutting down language server."); - - await _jsonRpc.NotifyWithParameterObjectAsync(Methods.WindowLogMessageName, message).ConfigureAwait(false); - - ShutdownImpl(); - ExitImpl(); - }).CompletesAsyncOperation(asyncToken); - } - - /// - /// Cleanup the server if we encounter a json rpc disconnect so that we can be restarted later. - /// - private void JsonRpc_Disconnected(object? sender, JsonRpcDisconnectedEventArgs e) - { - if (_shuttingDown) - { - // We're already in the normal shutdown -> exit path, no need to do anything. - return; - } - - _logger?.TraceWarning($"Encountered unexpected jsonrpc disconnect, Reason={e.Reason}, Description={e.Description}, Exception={e.Exception}"); - - ShutdownImpl(); - ExitImpl(); - } - - public async ValueTask DisposeAsync() - { - // if the server shut down due to error, we might not have finished cleaning up - if (_errorShutdownTask is not null) - await _errorShutdownTask.ConfigureAwait(false); - - if (_logger is IDisposable disposableLogger) - disposableLogger.Dispose(); - } - - internal TestAccessor GetTestAccessor() => new(this); - - internal readonly struct TestAccessor - { - private readonly LanguageServerTarget _server; - - internal TestAccessor(LanguageServerTarget server) - { - _server = server; - } - - public T GetRequiredLspService() where T : class, ILspService => _server._lspServices.GetRequiredService(); - - internal RequestExecutionQueue.TestAccessor GetQueueAccessor() - => _server._queue!.GetTestAccessor(); - - internal JsonRpc GetServerRpc() => _server._jsonRpc; - - internal bool HasShutdownStarted() => _server.HasShutdownStarted; - - internal void ShutdownServer() => _server.ShutdownImpl(); - - internal void ExitServer() => _server.ExitImpl(); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/LspServices/AbstractLspServiceProvider.cs b/src/Features/LanguageServer/Protocol/LspServices/AbstractLspServiceProvider.cs index a23a3f61d414a..53c6ae525c671 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/AbstractLspServiceProvider.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/AbstractLspServiceProvider.cs @@ -5,25 +5,27 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Text; -using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.CommonLanguageServerProtocol.Framework; namespace Microsoft.CodeAnalysis.LanguageServer; + internal class AbstractLspServiceProvider { private readonly ImmutableArray> _lspServices; private readonly ImmutableArray> _lspServiceFactories; public AbstractLspServiceProvider( - IEnumerable> lspServices, - IEnumerable> lspServiceFactories) + IEnumerable> specificLspServices, + IEnumerable> specificLspServiceFactories) { - _lspServices = lspServices.ToImmutableArray(); - _lspServiceFactories = lspServiceFactories.ToImmutableArray(); + _lspServices = specificLspServices.ToImmutableArray(); + _lspServiceFactories = specificLspServiceFactories.ToImmutableArray(); } - public LspServices CreateServices(WellKnownLspServerKinds serverKind, ImmutableArray> baseServices) + public LspServices CreateServices(WellKnownLspServerKinds serverKind, ImmutableDictionary>> baseServices) { - return new LspServices(_lspServices, _lspServiceFactories, serverKind, baseServices); + var lspServices = new LspServices(_lspServices, _lspServiceFactories, serverKind, baseServices); + + return lspServices; } } diff --git a/src/Features/LanguageServer/Protocol/LspServices/ExportStatelessLspServiceAttribute.cs b/src/Features/LanguageServer/Protocol/LspServices/ExportStatelessLspServiceAttribute.cs index 7b4ac98e2d954..2f9aa60264090 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/ExportStatelessLspServiceAttribute.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/ExportStatelessLspServiceAttribute.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer; /// /// MEF will dispose of these services when the container is disposed of. /// -[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false), MetadataAttribute] internal class ExportStatelessLspServiceAttribute : ExportAttribute { /// diff --git a/src/Features/LanguageServer/Protocol/LspServices/ILspServiceFactory.cs b/src/Features/LanguageServer/Protocol/LspServices/ILspServiceFactory.cs index 1e5c9979f5f1c..99008a8b8ceee 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/ILspServiceFactory.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/ILspServiceFactory.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.VisualStudio.LanguageServer.Protocol; - namespace Microsoft.CodeAnalysis.LanguageServer; internal interface ILspServiceFactory diff --git a/src/Features/LanguageServer/Protocol/LspServices/LspServices.cs b/src/Features/LanguageServer/Protocol/LspServices/LspServices.cs index 9f4a6dcc21eab..28ff250a8f449 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/LspServices.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/LspServices.cs @@ -5,18 +5,24 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.ComponentModel.Composition.Hosting; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer; -internal class LspServices : IDisposable +internal class LspServices : ILspServices { - private readonly ImmutableDictionary> _lazyLspServices; + private readonly ImmutableDictionary> _lazyMefLspServices; + + /// + /// A set of base services that apply to all roslyn lsp services. + /// Unfortunately MEF doesn't provide a good way to export something for multiple contracts with metadata + /// so these are manually created in . + /// TODO - cleanup once https://github.com/dotnet/roslyn/issues/63555 is resolved. + /// + private readonly ImmutableDictionary>> _baseServices; /// /// Gates access to . @@ -28,7 +34,7 @@ public LspServices( ImmutableArray> mefLspServices, ImmutableArray> mefLspServiceFactories, WellKnownLspServerKinds serverKind, - ImmutableArray> baseServices) + ImmutableDictionary>> baseServices) { // Convert MEF exported service factories to the lazy LSP services that they create. var servicesFromFactories = mefLspServiceFactories.Select(lz => new Lazy(() => lz.Value.CreateILspService(this, serverKind), lz.Metadata)); @@ -38,28 +44,45 @@ public LspServices( // Make sure that we only include services exported for the specified server kind (or NotSpecified). services = services.Where(lazyService => lazyService.Metadata.ServerKind == serverKind || lazyService.Metadata.ServerKind == WellKnownLspServerKinds.Any); - // Include the base level services that were passed in. - services = services.Concat(baseServices); + // This will throw if the same service is registered twice + _lazyMefLspServices = services.ToImmutableDictionary(lazyService => lazyService.Metadata.Type, lazyService => lazyService); - _lazyLspServices = services.ToImmutableDictionary(lazyService => lazyService.Metadata.Type, lazyService => lazyService); + // Bit cheaky, but lets make an this ILspService available on the base services to make constructors that take an ILspServices instance possible. + _baseServices = baseServices.Add(typeof(ILspServices), ImmutableArray.Create>((_) => this)); } - public T GetRequiredService() where T : class, ILspService + public T GetRequiredService() where T : notnull { - var service = GetService(); + T? service; + + // Check the base services first + service = GetBaseServices().SingleOrDefault(); + service ??= GetService(); + Contract.ThrowIfNull(service, $"Missing required LSP service {typeof(T).FullName}"); return service; } - public T? GetService() where T : class, ILspService + public T? GetService() { var type = typeof(T); - return TryGetService(type, out var service) ? (T)service : null; + var service = (T?)TryGetService(type); + + return service; } - public bool TryGetService(Type type, [NotNullWhen(true)] out object? lspService) + public IEnumerable GetRequiredServices() { - if (_lazyLspServices.TryGetValue(type, out var lazyService)) + var baseServices = GetBaseServices(); + var mefServices = GetMefServices(); + + return baseServices != null ? mefServices.Concat(baseServices) : mefServices; + } + + public object? TryGetService(Type type) + { + object? lspService; + if (_lazyMefLspServices.TryGetValue(type, out var lazyService)) { // If we are creating a stateful LSP service for the first time, we need to check // if it is disposable after creation and keep it around to dispose of on shutdown. @@ -75,14 +98,57 @@ public bool TryGetService(Type type, [NotNullWhen(true)] out object? lspService) } } - return true; + return lspService; } lspService = null; - return false; + return lspService; + } + + public ImmutableArray GetRegisteredServices() => _lazyMefLspServices.Keys.ToImmutableArray(); + + public bool SupportsGetRegisteredServices() + { + return true; + } + + private IEnumerable GetBaseServices() + { + if (_baseServices.TryGetValue(typeof(T), out var baseServices)) + { + return baseServices.Select(creatorFunc => (T)creatorFunc(this)).ToImmutableArray(); + } + + return ImmutableArray.Empty; } - public ImmutableArray GetRegisteredServices() => _lazyLspServices.Keys.ToImmutableArray(); + private IEnumerable GetMefServices() + { + if (typeof(T) == typeof(IMethodHandler)) + { + // HACK: There is special handling for the IMethodHandler to make sure that its types remain lazy + // Special case this to avoid providing them twice. + yield break; + } + + var allServices = GetRegisteredServices(); + foreach (var service in allServices) + { + var @interface = service.GetInterface(typeof(T).Name); + if (@interface is not null) + { + var instance = TryGetService(service); + if (instance is not null) + { + yield return (T)instance; + } + else + { + throw new Exception("Service failed to construct"); + } + } + } + } public void Dispose() { diff --git a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs index 0e9f3a314a906..b9abb74d7af29 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs @@ -17,7 +17,8 @@ internal class CSharpVisualBasicLspServiceProvider : AbstractLspServiceProvider [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpVisualBasicLspServiceProvider( [ImportMany(ProtocolConstants.RoslynLspLanguagesContract)] IEnumerable> lspServices, - [ImportMany(ProtocolConstants.RoslynLspLanguagesContract)] IEnumerable> lspServiceFactories) : base(lspServices, lspServiceFactories) + [ImportMany(ProtocolConstants.RoslynLspLanguagesContract)] IEnumerable> lspServiceFactories) + : base(lspServices, lspServiceFactories) { } } diff --git a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 8098a28ba3a80..0e9a542923f0a 100644 --- a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Features/LanguageServer/Protocol/NoOpLspLogger.cs b/src/Features/LanguageServer/Protocol/NoOpLspLogger.cs new file mode 100644 index 0000000000000..d1f492de309f0 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/NoOpLspLogger.cs @@ -0,0 +1,41 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.LanguageServer +{ + internal class NoOpLspLogger : ILspServiceLogger + { + public static readonly ILspServiceLogger Instance = new NoOpLspLogger(); + + private NoOpLspLogger() { } + + public void LogException(Exception exception, string? message = null, params object[] @params) + { + } + + public void LogInformation(string message, params object[] @params) + { + } + + public void LogWarning(string message, params object[] @params) + { + } + + public void LogError(string message, params object[] @params) + { + } + + public void LogStartContext(string message, params object[] @params) + { + } + + public void LogEndContext(string message, params object[] @params) + { + } + } +} diff --git a/src/Features/LanguageServer/Protocol/PublicAPI.Unshipped.txt b/src/Features/LanguageServer/Protocol/PublicAPI.Unshipped.txt index 8b137891791fe..e69de29bb2d1d 100644 --- a/src/Features/LanguageServer/Protocol/PublicAPI.Unshipped.txt +++ b/src/Features/LanguageServer/Protocol/PublicAPI.Unshipped.txt @@ -1 +0,0 @@ - diff --git a/src/Features/LanguageServer/Protocol/RequestDispatcher.cs b/src/Features/LanguageServer/Protocol/RequestDispatcher.cs deleted file mode 100644 index fae565289c16f..0000000000000 --- a/src/Features/LanguageServer/Protocol/RequestDispatcher.cs +++ /dev/null @@ -1,125 +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.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Roslyn.Utilities; -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer -{ - internal readonly record struct RequestHandlerMetadata(string MethodName, Type RequestType, Type ResponseType); - - /// - /// Aggregates handlers for the specified languages and dispatches LSP requests - /// to the appropriate handler for the request. - /// - internal class RequestDispatcher : ILspService - { - private readonly ImmutableDictionary> _requestHandlers; - - public RequestDispatcher(LspServices lspServices) - { - _requestHandlers = CreateMethodToHandlerMap(lspServices); - } - - private static ImmutableDictionary> CreateMethodToHandlerMap(LspServices lspServices) - { - var requestHandlerDictionary = ImmutableDictionary.CreateBuilder>(); - - var requestHandlerTypes = lspServices.GetRegisteredServices().Where(type => IsTypeRequestHandler(type)); - - foreach (var handlerType in requestHandlerTypes) - { - var (requestType, responseType) = ConvertHandlerTypeToRequestResponseTypes(handlerType); - var method = GetRequestHandlerMethod(handlerType); - - // Using the lazy set of handlers, create a lazy instance that will resolve the set of handlers for the provider - // and then lookup the correct handler for the specified method. - requestHandlerDictionary.Add(new RequestHandlerMetadata(method, requestType, responseType), new Lazy(() => - { - Contract.ThrowIfFalse(lspServices.TryGetService(handlerType, out var lspService)); - return (IRequestHandler)lspService; - })); - } - - return requestHandlerDictionary.ToImmutable(); - - static string GetRequestHandlerMethod(Type handlerType) - { - // Get the LSP method name from the handler's method name attribute. - var methodAttribute = Attribute.GetCustomAttribute(handlerType, typeof(MethodAttribute)) as MethodAttribute; - Contract.ThrowIfNull(methodAttribute, $"{handlerType.FullName} is missing Method attribute"); - - return methodAttribute.Method; - } - - static bool IsTypeRequestHandler(Type type) - { - return type.GetInterfaces().Contains(typeof(IRequestHandler)); - } - } - - /// - /// Retrieves the generic argument information from the request handler type without instantiating it. - /// - private static (Type requestType, Type responseType) ConvertHandlerTypeToRequestResponseTypes(Type handlerType) - { - var requestHandlerGenericType = handlerType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)).SingleOrDefault(); - Contract.ThrowIfNull(requestHandlerGenericType, $"Provided handler type {handlerType.FullName} does not implement IRequestHandler<,>"); - - var genericArguments = requestHandlerGenericType.GetGenericArguments(); - Contract.ThrowIfFalse(genericArguments.Length == 2, $"Provided handler type {handlerType.FullName} does not have exactly two generic arguments"); - var requestType = genericArguments[0]; - var responseType = genericArguments[1]; - - return (requestType, responseType); - } - - public async Task ExecuteRequestAsync( - string methodName, - TRequestType request, - LSP.ClientCapabilities clientCapabilities, - RequestExecutionQueue queue, - CancellationToken cancellationToken) where TRequestType : class - { - // Get the handler matching the requested method. - var requestHandlerMetadata = new RequestHandlerMetadata(methodName, typeof(TRequestType), typeof(TResponseType)); - - var handler = _requestHandlers[requestHandlerMetadata].Value; - - var mutatesSolutionState = handler.MutatesSolutionState; - var requiresLspSolution = handler.RequiresLSPSolution; - - var strongHandler = (IRequestHandler?)handler; - Contract.ThrowIfNull(strongHandler, string.Format("Request handler not found for method {0}", methodName)); - - var result = await ExecuteRequestAsync(queue, mutatesSolutionState, requiresLspSolution, strongHandler, request, clientCapabilities, methodName, cancellationToken).ConfigureAwait(false); - return result; - } - - protected virtual Task ExecuteRequestAsync( - RequestExecutionQueue queue, - bool mutatesSolutionState, - bool requiresLSPSolution, - IRequestHandler handler, - TRequestType request, - LSP.ClientCapabilities clientCapabilities, - string methodName, - CancellationToken cancellationToken) where TRequestType : class - { - return queue.ExecuteAsync(mutatesSolutionState, requiresLSPSolution, handler, request, clientCapabilities, methodName, cancellationToken); - } - - public ImmutableArray GetRegisteredMethods() - { - return _requestHandlers.Keys.ToImmutableArray(); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs b/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs deleted file mode 100644 index ac967b0166596..0000000000000 --- a/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs +++ /dev/null @@ -1,33 +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.Collections.Immutable; -using System.ComponentModel.Composition.Hosting; -using System.Composition; -using System.Linq; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.LanguageServer -{ - [ExportCSharpVisualBasicLspServiceFactory(typeof(RequestDispatcher)), Shared] - internal class RequestDispatcherFactory : ILspServiceFactory - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RequestDispatcherFactory() - { - } - - public virtual ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - return new RequestDispatcher(lspServices); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs new file mode 100644 index 0000000000000..172ff9132b922 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs @@ -0,0 +1,105 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Roslyn.Utilities; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.LanguageServer +{ + internal class RoslynLanguageServer : AbstractLanguageServer, IClientCapabilitiesProvider + { + private readonly AbstractLspServiceProvider _lspServiceProvider; + private readonly ImmutableDictionary>> _baseServices; + private readonly WellKnownLspServerKinds _serverKind; + + public RoslynLanguageServer( + AbstractLspServiceProvider lspServiceProvider, + JsonRpc jsonRpc, + ICapabilitiesProvider capabilitiesProvider, + ILspServiceLogger logger, + ImmutableArray supportedLanguages, + WellKnownLspServerKinds serverKind) + : base(jsonRpc, logger) + { + _lspServiceProvider = lspServiceProvider; + _serverKind = serverKind; + + // Create services that require base dependencies (jsonrpc) or are more complex to create to the set manually. + _baseServices = GetBaseServices(jsonRpc, this, logger, capabilitiesProvider, serverKind, supportedLanguages); + + // This spins up the queue and ensure the LSP is ready to start receiving requests + Initialize(); + } + + protected override ILspServices ConstructLspServices() + { + return _lspServiceProvider.CreateServices(_serverKind, _baseServices); + } + + protected override IRequestExecutionQueue ConstructRequestExecutionQueue() + { + var handlerProvider = GetHandlerProvider(); + var queue = new RoslynRequestExecutionQueue(this, _logger, handlerProvider); + + queue.Start(); + return queue; + } + + private ImmutableDictionary>> GetBaseServices( + JsonRpc jsonRpc, + IClientCapabilitiesProvider clientCapabilitiesProvider, + ILspServiceLogger logger, + ICapabilitiesProvider capabilitiesProvider, + WellKnownLspServerKinds serverKind, + ImmutableArray supportedLanguages) + { + var baseServices = new Dictionary>>(); + var clientLanguageServerManager = new ClientLanguageServerManager(jsonRpc); + var lifeCycleManager = new LspServiceLifeCycleManager(clientLanguageServerManager); + + AddBaseService(clientLanguageServerManager); + AddBaseService(logger); + AddBaseService(logger); + AddBaseService(clientCapabilitiesProvider); + AddBaseService(capabilitiesProvider); + AddBaseService(lifeCycleManager); + AddBaseService(new ServerInfoProvider(serverKind, supportedLanguages)); + AddBaseServiceFromFunc>((lspServices) => new RequestContextFactory(lspServices)); + AddBaseServiceFromFunc>((_) => GetRequestExecutionQueue()); + AddBaseService(new ClientCapabilitiesManager()); + AddBaseService(new InitializeHandler()); + AddBaseService(new InitializedHandler()); + + return baseServices.ToImmutableDictionary(); + + void AddBaseService(T instance) where T : class + { + AddBaseServiceFromFunc((_) => instance); + } + + void AddBaseServiceFromFunc(Func creatorFunc) + { + var added = baseServices.GetValueOrDefault(typeof(T), ImmutableArray>.Empty).Add(creatorFunc); + baseServices[typeof(T)] = added; + } + } + + public ClientCapabilities GetClientCapabilities() + { + var lspServices = GetLspServices(); + var clientCapabilitiesManager = lspServices.GetRequiredService(); + var clientCapabilities = clientCapabilitiesManager.GetClientCapabilities(); + + return clientCapabilities; + } + } +} diff --git a/src/Features/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs b/src/Features/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs new file mode 100644 index 0000000000000..d782ff5b6a838 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/RoslynRequestExecutionQueue.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 System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer +{ + internal class RoslynRequestExecutionQueue : RequestExecutionQueue + { + public RoslynRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, IHandlerProvider handlerProvider) + : base(languageServer, logger, handlerProvider) + { + } + + public override Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + { + if (rethrowExceptions) + { + return nonMutatingRequestTask; + } + else + { + return nonMutatingRequestTask.ReportNonFatalErrorAsync(); + } + } + } +} diff --git a/src/Features/LanguageServer/Protocol/WellKnownLspServerKinds.cs b/src/Features/LanguageServer/Protocol/WellKnownLspServerKinds.cs index 12054488eb478..a99f84b743393 100644 --- a/src/Features/LanguageServer/Protocol/WellKnownLspServerKinds.cs +++ b/src/Features/LanguageServer/Protocol/WellKnownLspServerKinds.cs @@ -2,9 +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 System; -using System.Collections.Generic; -using System.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 1cc824c529c35..41d2c7bda8ca6 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -5,17 +5,16 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer { @@ -57,13 +56,13 @@ public LspMiscellaneousFilesWorkspace() : base(MefHostServices.DefaultHost, Work if (!s_extensionToLanguageInformation.TryGetValue(Path.GetExtension(uriAbsolutePath), out var languageInformation)) { // Only log here since throwing here could take down the LSP server. - logger.TraceError($"Could not find language information for {uri} with absolute path {uriAbsolutePath}"); + logger.LogError($"Could not find language information for {uri} with absolute path {uriAbsolutePath}"); return null; } 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; @@ -93,7 +92,7 @@ public void TryRemoveMiscellaneousDocument(Uri uri) } } - private class SourceTextLoader : TextLoader + private sealed class SourceTextLoader : TextLoader { private readonly SourceText _sourceText; private readonly string _fileUri; @@ -104,7 +103,11 @@ public SourceTextLoader(SourceText sourceText, string fileUri) _fileUri = fileUri; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override string? FilePath + => _fileUri; + + // TODO (https://github.com/dotnet/roslyn/issues/63583): Use options.ChecksumAlgorithm + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_sourceText, VersionStamp.Create(), _fileUri)); } } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index f0a327ffee2d5..60313800d4e63 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -5,21 +5,18 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.LanguageServer.Handler.RequestExecutionQueue; namespace Microsoft.CodeAnalysis.LanguageServer; @@ -53,7 +50,7 @@ internal class LspWorkspaceManager : IDocumentChangeTracker, ILspService /// The forkedFromVersion is not null when the solution was created from a fork of the workspace with LSP text applied on top. /// It is null when LSP reuses the workspace solution (the LSP text matches the contents of the workspace). /// - /// Access to this is gauranteed to be serial by the + /// Access to this is gauranteed to be serial by the /// private readonly Dictionary _cachedLspSolutions = new(); @@ -65,7 +62,7 @@ internal class LspWorkspaceManager : IDocumentChangeTracker, ILspService /// Note that the text here is tracked regardless of whether or not we found a matching roslyn document /// for the URI. /// - /// Access to this is gauranteed to be serial by the + /// Access to this is gauranteed to be serial by the /// private ImmutableDictionary _trackedDocuments = ImmutableDictionary.Empty; @@ -95,7 +92,7 @@ public LspWorkspaceManager( /// /// Called by the when a document is opened in LSP. /// - /// is true which means this runs serially in the + /// is true which means this runs serially in the /// public void StartTracking(Uri uri, SourceText documentText) { @@ -110,7 +107,7 @@ public void StartTracking(Uri uri, SourceText documentText) /// /// Called by the when a document is closed in LSP. /// - /// is true which means this runs serially in the + /// is true which means this runs serially in the /// public void StopTracking(Uri uri) { @@ -128,7 +125,7 @@ public void StopTracking(Uri uri) /// /// Called by the when a document's text is updated in LSP. /// - /// is true which means this runs serially in the + /// is true which means this runs serially in the /// public void UpdateTrackedDocument(Uri uri, SourceText newSourceText) { @@ -151,7 +148,7 @@ public void UpdateTrackedDocument(Uri uri, SourceText newSourceText) /// is the solution used for LSP requests that pertain to the entire workspace, for example code search or workspace /// diagnostics. /// - /// This is always called serially in the when creating the . + /// This is always called serially in the when creating the . /// public async Task<(Workspace?, Solution?)> GetLspSolutionInfoAsync(CancellationToken cancellationToken) { @@ -165,41 +162,41 @@ public void UpdateTrackedDocument(Uri uri, SourceText newSourceText) } /// - /// Returns a document with the LSP tracked text forked from the appropriate workspace solution. + /// Returns the LSP solution associated with the workspace with the specified . This + /// is the solution used for LSP requests that pertain to the entire workspace, for example code search or workspace + /// diagnostics. /// - /// This is always called serially in the when creating the . + /// This is always called serially in the when creating the . /// public async Task<(Workspace?, Solution?, Document?)> GetLspDocumentInfoAsync(TextDocumentIdentifier textDocumentIdentifier, CancellationToken cancellationToken) { - var uri = textDocumentIdentifier.Uri; - // Get the LSP view of all the workspace solutions. var lspSolutions = await GetLspSolutionsAsync(cancellationToken).ConfigureAwait(false); // Find the matching document from the LSP solutions. foreach (var (workspace, lspSolution, isForked) in lspSolutions) { - var documents = lspSolution.GetDocuments(uri); + var documents = lspSolution.GetDocuments(textDocumentIdentifier.Uri); if (documents.Any()) { - var document = documents.FindDocumentInProjectContext(textDocumentIdentifier); + var document = documents.FindDocumentInProjectContext(textDocumentIdentifier, (sln, id) => sln.GetRequiredDocument(id)); // Record metadata on how we got this document. var workspaceKind = document.Project.Solution.WorkspaceKind; _requestTelemetryLogger.UpdateFindDocumentTelemetryData(success: true, workspaceKind); _requestTelemetryLogger.UpdateUsedForkedSolutionCounter(isForked); - _logger.TraceInformation($"{document.FilePath} found in workspace {workspaceKind}"); + _logger.LogInformation($"{document.FilePath} found in workspace {workspaceKind}"); return (workspace, document.Project.Solution, document); } } // We didn't find the document in any workspace, record a telemetry notification that we did not find it. - var searchedWorkspaceKinds = string.Join(";", lspSolutions.SelectAsArray(lspSolution => lspSolution.Solution.WorkspaceKind)); - _logger.TraceError($"Could not find '{uri}'. Searched {searchedWorkspaceKinds}"); + var searchedWorkspaceKinds = string.Join(";", lspSolutions.SelectAsArray(lspSolution => lspSolution.Solution.Workspace.Kind)); + _logger.LogError($"Could not find '{textDocumentIdentifier.Uri}'. Searched {searchedWorkspaceKinds}"); _requestTelemetryLogger.UpdateFindDocumentTelemetryData(success: false, workspaceKind: null); + var uri = textDocumentIdentifier.Uri; // Add the document to our loose files workspace if its open. if (_trackedDocuments.ContainsKey(uri)) { @@ -310,7 +307,7 @@ private async Task DoesAllTextMatchWorkspaceSolutionAsync(ImmutableDiction if (!isTextEquivalent) { - _logger.TraceWarning($"Text for {uriInWorkspace} did not match document text {firstDocument.Id} in workspace's {firstDocument.Project.Solution.WorkspaceKind} current solution"); + _logger.LogWarning($"Text for {uriInWorkspace} did not match document text {firstDocument.Id} in workspace's {firstDocument.Project.Solution.WorkspaceKind} current solution"); return false; } } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManagerFactory.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManagerFactory.cs index 70c4abd832a50..762167502abb6 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManagerFactory.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManagerFactory.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -23,7 +24,7 @@ public LspWorkspaceManagerFactory(LspWorkspaceRegistrationService lspWorkspaceRe public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { - var logger = lspServices.GetRequiredService(); + var logger = lspServices.GetRequiredService(); var telemetryLogger = lspServices.GetRequiredService(); var miscFilesWorkspace = lspServices.GetService(); return new LspWorkspaceManager(logger, miscFilesWorkspace, _workspaceRegistrationService, telemetryLogger); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index 773c05c3077c3..907a931771b3f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -12,12 +12,17 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions { public class CodeActionResolveTests : AbstractLanguageServerProtocolTests { + public CodeActionResolveTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [WpfFact] public async Task TestCodeActionResolveHandlerAsync() { @@ -29,7 +34,7 @@ void M() {|caret:|}int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(initialMarkup); + await using var testLspServer = await CreateTestLspServerAsync(initialMarkup); var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( title: CSharpAnalyzersResources.Use_implicit_type, @@ -80,7 +85,7 @@ void M() int {|caret:|}i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(initialMarkup); + await using var testLspServer = await CreateTestLspServerAsync(initialMarkup); var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( title: string.Format(FeaturesResources.Introduce_constant_for_0, "1"), diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 15cab83dbddcf..ed1924ca59726 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -25,12 +25,17 @@ using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions { public class CodeActionsTests : AbstractLanguageServerProtocolTests { + public CodeActionsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [WpfFact] public async Task TestCodeActionHandlerAsync() { @@ -42,7 +47,7 @@ void M() {|caret:|}int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caretLocation = testLspServer.GetLocations("caret").Single(); var expected = CreateCodeAction( @@ -75,7 +80,7 @@ void M() int {|caret:|}i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); var expected = CreateCodeAction( @@ -111,7 +116,7 @@ void M() {|caret:|}int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var cache = GetCodeActionsCache(testLspServer); var testAccessor = cache.GetTestAccessor(); @@ -201,7 +206,7 @@ void M() {|caret:|}Task.Delay(1); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caret = testLspServer.GetLocations("caret").Single(); var codeActionParams = new LSP.CodeActionParams diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index dd6d69153b211..d7d753843da1c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -13,12 +13,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions { public class RunCodeActionsTests : AbstractLanguageServerProtocolTests { + public RunCodeActionsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [WpfFact] public async Task TestRunCodeActions() { @@ -38,7 +43,7 @@ class B } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caretLocation = testLspServer.GetLocations("caret").Single(); var commandArgument = new CodeActionResolveData(string.Format(FeaturesResources.Move_type_to_0, "B.cs"), customTags: ImmutableArray.Empty, caretLocation.Range, new LSP.TextDocumentIdentifier diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs index 741f82e644286..676f922e37894 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -14,18 +13,24 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.Text.Adornments; using Newtonsoft.Json; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Completion { public class CompletionResolveTests : AbstractLanguageServerProtocolTests { + public CompletionResolveTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestResolveCompletionItemFromListAsync() { @@ -52,7 +57,7 @@ void M() } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, @@ -77,7 +82,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); + await using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "A").ConfigureAwait(false); var description = new ClassifiedTextElement(CreateClassifiedTextRunForClass("A")); @@ -101,7 +106,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); + await using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "M()").ConfigureAwait(false); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( testLspServer, clientCompletionItem).ConfigureAwait(false); @@ -143,7 +148,7 @@ class B : A } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "M()").ConfigureAwait(false); @@ -172,7 +177,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); @@ -235,7 +240,7 @@ void M() } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); @@ -297,7 +302,7 @@ void M() AMet{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); @@ -332,7 +337,7 @@ void M() a.{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); + await using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "(byte)").ConfigureAwait(false); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( @@ -428,7 +433,7 @@ private static T ConvertToClientCompletionItem(T serverCompletionItem) where private class TestCaretOutOfScopeCompletionService : CompletionService { - public TestCaretOutOfScopeCompletionService(SolutionServices services) : base(services) + public TestCaretOutOfScopeCompletionService(SolutionServices services) : base(services, AsynchronousOperationListenerProvider.NullProvider) { } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index 3a494c0f0d082..ccc21b29886c9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Options; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Completion @@ -37,6 +38,10 @@ public class CompletionTests : AbstractLanguageServerProtocolTests } }; + public CompletionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetCompletionsAsync_PromotesCommitCharactersToListAsync() { @@ -66,7 +71,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -113,7 +118,7 @@ public async Task TestGetCompletions_PromotesNothingWhenNoCommitCharactersAsync( @"namespace M {{|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -146,7 +151,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -174,7 +179,7 @@ void M() A{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -201,7 +206,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var solution = testLspServer.TestWorkspace.CurrentSolution; // Make sure the unimported types option is on by default. @@ -226,7 +231,7 @@ public async Task TestGetCompletionsUsesSnippetOptionAsync() {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption( new OptionKey(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.CSharp), SnippetsRule.NeverInclude); @@ -252,7 +257,7 @@ void M() A classA = new {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -286,7 +291,7 @@ void M() } } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -310,7 +315,7 @@ void M() DateTime.Now.ToString(""{|caret:|}); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -338,7 +343,7 @@ void M() Guid.NewGuid().ToString(""{|caret:|}); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -363,7 +368,7 @@ void M() new Regex(""{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -400,7 +405,7 @@ void M() new Regex(@""\{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -437,7 +442,7 @@ void M() Regex r = new(""\\{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -489,7 +494,7 @@ void M() new Regex(""{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -521,7 +526,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var cache = GetCompletionListCache(testLspServer); Assert.NotNull(cache); @@ -584,7 +589,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Deletion, @@ -615,7 +620,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -643,7 +648,7 @@ partial class C { partial {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -669,7 +674,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -696,7 +701,7 @@ void M() new Regex(""[{|caret:|}"") } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -748,7 +753,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -800,7 +805,7 @@ void M() W someW = new {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); var completionParams = CreateCompletionParams( @@ -853,7 +858,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -919,7 +924,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -985,7 +990,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -1080,7 +1085,7 @@ void M2() Console.W{|secondCaret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var firstCaret = testLspServer.GetLocations("firstCaret").Single(); await testLspServer.OpenDocumentAsync(firstCaret.Uri); @@ -1144,7 +1149,7 @@ void M() Ta{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); var completionParams = CreateCompletionParams( @@ -1203,7 +1208,7 @@ void M2() {|secondCaret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var firstCaretLocation = testLspServer.GetLocations("firstCaret").Single(); await testLspServer.OpenDocumentAsync(firstCaretLocation.Uri); @@ -1275,7 +1280,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -1329,7 +1334,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, s_vsCompletionCapabilities); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs index 8cfc336fe976f..ffa1313c8b378 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs @@ -11,12 +11,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Definitions { public class GoToDefinitionTests : AbstractLanguageServerProtocolTests { + public GoToDefinitionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGotoDefinitionAsync() { @@ -29,7 +34,7 @@ void M() var len = {|caret:|}aString.Length; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("definition"), results); @@ -56,7 +61,7 @@ class B }" }; - using var testLspServer = await CreateTestLspServerAsync(markups); + await using var testLspServer = await CreateTestLspServerAsync(markups); var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("definition"), results); @@ -74,7 +79,7 @@ void M() var len = aString.Length; } }"; - using var testLspServer = await CreateTestLspServerAsync(string.Empty); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty); AddMappedDocument(testLspServer.TestWorkspace, markup); @@ -98,7 +103,7 @@ void M() var len = aString.Length; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); Assert.Empty(results); @@ -114,7 +119,7 @@ class A { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); Assert.Empty(results); @@ -141,7 +146,7 @@ End Class "; - using var testLspServer = await CreateXmlTestLspServerAsync(markup); + await using var testLspServer = await CreateXmlTestLspServerAsync(markup); var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("definition"), results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs index 9176786d78648..660659ec20c5d 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs @@ -9,12 +9,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Definitions { public class GoToTypeDefinitionTests : AbstractLanguageServerProtocolTests { + public GoToTypeDefinitionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGotoTypeDefinitionAsync() { @@ -26,7 +31,7 @@ class B { {|caret:|}A classA; }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("definition"), results); @@ -52,7 +57,7 @@ class B }" }; - using var testLspServer = await CreateTestLspServerAsync(markups); + await using var testLspServer = await CreateTestLspServerAsync(markups); var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("definition"), results); @@ -70,7 +75,7 @@ class B A classA; {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); Assert.Empty(results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index c77e7816b1f29..b9b02253d7baa 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -22,6 +22,7 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics @@ -30,19 +31,20 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics public abstract class AbstractPullDiagnosticTestsBase : AbstractLanguageServerProtocolTests { - private protected override TestAnalyzerReferenceByLanguage TestAnalyzerReferences + protected AbstractPullDiagnosticTestsBase(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { - get - { - var builder = ImmutableDictionary.CreateBuilder>(); - builder.Add(LanguageNames.CSharp, ImmutableArray.Create( - DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), - new CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer(), - new CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer())); - builder.Add(LanguageNames.VisualBasic, ImmutableArray.Create(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic))); - builder.Add(InternalLanguageNames.TypeScript, ImmutableArray.Create(new MockTypescriptDiagnosticAnalyzer())); - return new(builder.ToImmutableDictionary()); - } + } + + private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() + { + var builder = ImmutableDictionary.CreateBuilder>(); + builder.Add(LanguageNames.CSharp, ImmutableArray.Create( + DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), + new CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer(), + new CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer())); + builder.Add(LanguageNames.VisualBasic, ImmutableArray.Create(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic))); + builder.Add(InternalLanguageNames.TypeScript, ImmutableArray.Create(new MockTypescriptDiagnosticAnalyzer())); + return new(builder.ToImmutableDictionary()); } protected override TestComposition Composition => base.Composition.AddParts(typeof(MockTypescriptDiagnosticAnalyzer)); @@ -50,11 +52,11 @@ private protected override TestAnalyzerReferenceByLanguage TestAnalyzerReference private protected static async Task> RunGetWorkspacePullDiagnosticsAsync( TestLspServer testLspServer, bool useVSDiagnostics, - ImmutableArray<(string resultId, Uri uri)>? previousResults = null, + ImmutableArray<(string resultId, TextDocumentIdentifier identifier)>? previousResults = null, bool useProgress = false, bool includeTaskListItems = false) { - var optionService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + var optionService = testLspServer.TestWorkspace.GetService(); optionService.SetGlobalOption(new OptionKey(TaskListOptionsStorage.ComputeTaskListItemsForClosedFiles), includeTaskListItems); await testLspServer.WaitForDiagnosticsAsync(); @@ -73,7 +75,7 @@ private protected static async Task> RunGet } AssertEx.NotNull(diagnostics); - return diagnostics.Select(d => new TestDiagnosticResult(d.TextDocument!.Uri, d.ResultId!, d.Diagnostics)).ToImmutableArray(); + return diagnostics.Select(d => new TestDiagnosticResult(d.TextDocument!, d.ResultId!, d.Diagnostics)).ToImmutableArray(); } else { @@ -97,10 +99,10 @@ private protected static async Task> RunGet } static WorkspaceDiagnosticParams CreateProposedWorkspaceDiagnosticParams( - ImmutableArray<(string resultId, Uri uri)>? previousResults = null, + ImmutableArray<(string resultId, TextDocumentIdentifier identifier)>? previousResults = null, IProgress? progress = null) { - var previousResultsLsp = previousResults?.Select(r => new PreviousResultId(r.uri, r.resultId)).ToArray() ?? Array.Empty(); + var previousResultsLsp = previousResults?.Select(r => new PreviousResultId(r.identifier.Uri, r.resultId)).ToArray() ?? Array.Empty(); return new WorkspaceDiagnosticParams(identifier: null, previousResultsLsp, workDoneToken: null, partialResultToken: progress); } @@ -108,21 +110,22 @@ static TestDiagnosticResult ConvertWorkspaceDiagnosticResult(SumType testLspServer.CloseDocumentAsync(document.GetURI()); - private protected static ImmutableArray<(string resultId, Uri uri)> CreateDiagnosticParamsFromPreviousReports(ImmutableArray results) + private protected static ImmutableArray<(string resultId, TextDocumentIdentifier identifier)> CreateDiagnosticParamsFromPreviousReports(ImmutableArray results) { - return results.Select(r => (r.ResultId, r.Uri)).ToImmutableArray(); + + return results.Select(r => (r.ResultId, r.TextDocument)).ToImmutableArray(); } private protected static VSInternalDocumentDiagnosticsParams CreateDocumentDiagnosticParams( @@ -139,12 +142,12 @@ private protected static VSInternalDocumentDiagnosticsParams CreateDocumentDiagn } private protected static VSInternalWorkspaceDiagnosticsParams CreateWorkspaceDiagnosticParams( - ImmutableArray<(string resultId, Uri uri)>? previousResults = null, + ImmutableArray<(string resultId, TextDocumentIdentifier identifier)>? previousResults = null, IProgress? progress = null) { return new VSInternalWorkspaceDiagnosticsParams { - PreviousResults = previousResults?.Select(r => new VSInternalDiagnosticParams { PreviousResultId = r.resultId, TextDocument = new TextDocumentIdentifier { Uri = r.uri } }).ToArray(), + PreviousResults = previousResults?.Select(r => new VSInternalDiagnosticParams { PreviousResultId = r.resultId, TextDocument = r.identifier }).ToArray(), PartialResultToken = progress, }; } @@ -197,7 +200,7 @@ private protected static async Task> RunGet } AssertEx.NotNull(diagnostics); - return diagnostics.Select(d => new TestDiagnosticResult(vsTextDocumentIdentifier.Uri, d.ResultId!, d.Diagnostics)).ToImmutableArray(); + return diagnostics.Select(d => new TestDiagnosticResult(vsTextDocumentIdentifier, d.ResultId!, d.Diagnostics)).ToImmutableArray(); } else { @@ -220,11 +223,11 @@ private protected static async Task> RunGet else if (diagnostics.Value.Value is UnchangedDocumentDiagnosticReport) { // The public LSP spec returns different types when unchanged in contrast to VS which just returns null diagnostic array. - return ImmutableArray.Create(new TestDiagnosticResult(vsTextDocumentIdentifier.Uri, diagnostics.Value.Second.ResultId!, null)); + return ImmutableArray.Create(new TestDiagnosticResult(vsTextDocumentIdentifier, diagnostics.Value.Second.ResultId!, null)); } else { - return ImmutableArray.Create(new TestDiagnosticResult(vsTextDocumentIdentifier.Uri, diagnostics.Value.First.ResultId!, diagnostics.Value.First.Items)); + return ImmutableArray.Create(new TestDiagnosticResult(vsTextDocumentIdentifier, diagnostics.Value.First.ResultId!, diagnostics.Value.First.Items)); } } @@ -272,9 +275,9 @@ private protected static InitializationOptions GetInitializationOptions( /// Helper type to store unified LSP diagnostic results. /// Diagnostics are null when unchanged. /// - private protected record TestDiagnosticResult(Uri Uri, string ResultId, LSP.Diagnostic[]? Diagnostics) + private protected record TestDiagnosticResult(TextDocumentIdentifier TextDocument, string ResultId, LSP.Diagnostic[]? Diagnostics) { - public TextDocumentIdentifier TextDocument { get; } = new TextDocumentIdentifier { Uri = Uri }; + public Uri Uri { get; } = TextDocument.Uri; } [DiagnosticAnalyzer(InternalLanguageNames.TypeScript), PartNotDiscoverable] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs index 1fdaf6583bbae..8cc35ccb20532 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs @@ -12,12 +12,18 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics; public class AdditionalFileDiagnosticsTests : AbstractPullDiagnosticTestsBase { + public AdditionalFileDiagnosticsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsReportsAdditionalFileDiagnostic(bool useVSDiagnostics) { @@ -29,15 +35,15 @@ public async Task TestWorkspaceDiagnosticsReportsAdditionalFileDiagnostic(bool u "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(3, results.Length); - - Assert.Empty(results[0].Diagnostics); - Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); - Assert.Equal(@"C:\Test.txt", results[1].Uri.LocalPath); - Assert.Empty(results[2].Diagnostics); + AssertEx.Equal(new[] + { + @"C:\C.cs: []", + @$"C:\Test.txt: [{MockAdditionalFileDiagnosticAnalyzer.Id}]", + @"C:\CSProj1.csproj: []" + }, results.Select(r => $"{r.Uri.LocalPath}: [{string.Join(", ", r.Diagnostics.Select(d => d.Code?.Value?.ToString()))}]")); // Asking again should give us back an unchanged diagnostic. var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); @@ -58,7 +64,7 @@ public async Task TestWorkspaceDiagnosticsWithRemovedAdditionalFile(bool useVSDi "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); @@ -86,10 +92,45 @@ public async Task TestWorkspaceDiagnosticsWithRemovedAdditionalFile(bool useVSDi Assert.NotNull(results2[2].ResultId); } + [Fact] + public async Task TestWorkspaceDiagnosticsWithAdditionalFileInMultipleProjects() + { + var workspaceXml = +@$" + + + + + + + + +"; + + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics: true); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true); + Assert.Equal(6, results.Length); + + Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); + Assert.Equal(@"C:\Test.txt", results[1].Uri.LocalPath); + Assert.Equal("CSProj1", ((LSP.VSDiagnostic)results[1].Diagnostics.Single()).Projects.First().ProjectName); + Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[4].Diagnostics.Single().Code); + Assert.Equal(@"C:\Test.txt", results[4].Uri.LocalPath); + Assert.Equal("CSProj2", ((LSP.VSDiagnostic)results[4].Diagnostics.Single()).Projects.First().ProjectName); + + // Asking again should give us back an unchanged diagnostic. + var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); + Assert.Equal(results[1].ResultId, results2[1].ResultId); + Assert.Equal(results[4].ResultId, results2[4].ResultId); + } + protected override TestComposition Composition => base.Composition.AddParts(typeof(MockAdditionalFileDiagnosticAnalyzer)); - private protected override TestAnalyzerReferenceByLanguage TestAnalyzerReferences => new(ImmutableDictionary.Create>() - .Add(LanguageNames.CSharp, ImmutableArray.Create(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), new MockAdditionalFileDiagnosticAnalyzer()))); + private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() + => new(ImmutableDictionary>.Empty.Add(LanguageNames.CSharp, ImmutableArray.Create( + DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), + new MockAdditionalFileDiagnosticAnalyzer()))); [DiagnosticAnalyzer(LanguageNames.CSharp), PartNotDiscoverable] private class MockAdditionalFileDiagnosticAnalyzer : DiagnosticAnalyzer diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 48098883d08cc..1ce730a235807 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -27,12 +27,16 @@ using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics { public class PullDiagnosticTests : AbstractPullDiagnosticTestsBase { + public PullDiagnosticTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } #region Document Diagnostics [Theory, CombinatorialData] @@ -40,7 +44,7 @@ public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDi { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -54,7 +58,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagno { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -78,7 +82,7 @@ public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile(bool useVSDiagn // todo: goo class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -99,7 +103,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesWithFSAOffIfInPushMode(bo { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: false); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: false); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -116,7 +120,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOf { var markup = @"class A {"; - using var testLspServer = await CreateTestLspServerAsync(markup, + await using var testLspServer = await CreateTestLspServerAsync(markup, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); // Calling GetTextBuffer will effectively open the file. @@ -135,7 +139,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b { var markup = @"class A {"; - using var testLspServer = await CreateTestLspServerAsync(markup, + await using var testLspServer = await CreateTestLspServerAsync(markup, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Default)); // Calling GetTextBuffer will effectively open the file. @@ -154,7 +158,7 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var workspace = testLspServer.TestWorkspace; // Calling GetTextBuffer will effectively open the file. @@ -186,7 +190,7 @@ public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnos { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -213,7 +217,7 @@ public async Task TestDocumentDiagnosticsWhenEnCVersionChanges(bool useVSDiagnos { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -246,7 +250,7 @@ public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiag { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -269,7 +273,7 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -298,7 +302,7 @@ public async Task TestDocumentDiagnosticsAreNotMapped(bool useVSDiagnostics) var markup = @"#line 1 ""test.txt"" class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -319,7 +323,7 @@ public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics) { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -351,7 +355,7 @@ class B {"; "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -400,7 +404,7 @@ public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile() "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -454,7 +458,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -500,7 +504,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -530,7 +534,7 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics) @"class A {"; // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. - using var testLspServer = await CreateTestLspServerAsync(markup, + await using var testLspServer = await CreateTestLspServerAsync(markup, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Push, WellKnownLspServerKinds.RazorLspServer)); // Calling GetTextBuffer will effectively open the file. @@ -555,7 +559,7 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti @"class A {"; // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. - using var testLspServer = await CreateTestLspServerAsync(markup, + await using var testLspServer = await CreateTestLspServerAsync(markup, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, DiagnosticMode.Push, WellKnownLspServerKinds.LiveShareLspServer)); // Calling GetTextBuffer will effectively open the file. @@ -577,7 +581,7 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti public async Task TestDocumentDiagnosticsIncludesSourceGeneratorDiagnostics(bool useVSDiagnostics) { var markup = "// Hello, World"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -609,7 +613,7 @@ public async Task TestDocumentDiagnosticsWithFadingOptionOn(bool useVSDiagnostic class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var firstLocation = testLspServer.GetLocations("first").Single().Range; testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp), true); @@ -639,7 +643,7 @@ public async Task TestDocumentDiagnosticsWithFadingOptionOff(bool useVSDiagnosti class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var firstLocation = testLspServer.GetLocations("first").Single().Range; testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp), false); @@ -665,7 +669,7 @@ void M() 3 + 4{|close:)|}; } }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var openLocation = testLspServer.GetLocations("open").Single().Range; var closeLocation = testLspServer.GetLocations("close").Single().Range; var lineLocation = testLspServer.GetLocations("line").Single().Range; @@ -700,7 +704,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSD var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -714,7 +718,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiag var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -733,7 +737,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOff(bool useVS // todo: goo class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1 }, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false); @@ -749,7 +753,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn(bool useVSD // todo: goo class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1 }, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true); @@ -767,7 +771,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOff(bool useVSD // todo: goo class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: false); @@ -787,7 +791,7 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOn(bool useVSDi // todo: goo class A { }"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true); @@ -807,7 +811,7 @@ public async Task TestWorkspaceTodoAndDiagnosticForClosedFilesWithFSAOnAndTodoOn // todo: goo class A { "; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, includeTaskListItems: true); @@ -827,7 +831,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInPr var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true); var firstDocument = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); @@ -842,7 +846,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInPr public async Task TestWorkspaceDiagnosticsIncludesSourceGeneratorDiagnosticsClosedFSAOn(bool useVSDiagnostics) { var markup = "// Hello, World"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.FullSolution, useVSDiagnostics, pullDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.FullSolution, useVSDiagnostics, pullDiagnostics: true); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -862,7 +866,7 @@ public async Task TestWorkspaceDiagnosticsIncludesSourceGeneratorDiagnosticsClos public async Task TestWorkspaceDiagnosticsDoesNotIncludeSourceGeneratorDiagnosticsClosedFSAOffAndNoFilesOpen(bool useVSDiagnostics) { var markup = "// Hello, World"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: true); var generator = new DiagnosticProducingGenerator( context => Location.Create( @@ -883,7 +887,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOnAndInPushMode var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics, pullDiagnostics: false); await Assert.ThrowsAsync(async () => await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics)); @@ -906,7 +910,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -919,7 +923,7 @@ public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiag var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync( + await using var testLspServer = await CreateTestLspServerAsync( markups: Array.Empty(), GetInitializationOptions(BackgroundAnalysisScope.FullSolution, useVSDiagnostics, DiagnosticMode.Pull, sourceGeneratedMarkups: new[] { markup1, markup2 })); @@ -943,7 +947,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -975,7 +979,7 @@ public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagno var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1003,7 +1007,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1036,7 +1040,7 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1076,7 +1080,7 @@ public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics) var markup1 = @"class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1097,7 +1101,7 @@ public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics) @"#line 1 ""test.txt"" class A {"; var markup2 = ""; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -1134,7 +1138,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1206,7 +1210,7 @@ public class {|caret:|} "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj3Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj3").Single().Documents.First(); // Verify we have a diagnostic in C.cs initially. @@ -1274,7 +1278,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1337,7 +1341,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1392,7 +1396,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1448,7 +1452,7 @@ class A : B { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs index 9a10419dedf31..5388ace877f4a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs @@ -13,15 +13,20 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics; public class WorkspaceProjectDiagnosticsTests : AbstractPullDiagnosticTestsBase { + public WorkspaceProjectDiagnosticsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsReportsProjectDiagnostic(bool useVSDiagnostics) { - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(string.Empty, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(string.Empty, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -40,7 +45,7 @@ public async Task TestWorkspaceDiagnosticsReportsProjectDiagnostic(bool useVSDia [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsWithRemovedProject(bool useVSDiagnostics) { - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(string.Empty, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(string.Empty, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -63,8 +68,10 @@ public async Task TestWorkspaceDiagnosticsWithRemovedProject(bool useVSDiagnosti protected override TestComposition Composition => base.Composition.AddParts(typeof(MockProjectDiagnosticAnalyzer)); - private protected override TestAnalyzerReferenceByLanguage TestAnalyzerReferences => new(ImmutableDictionary.Create>() - .Add(LanguageNames.CSharp, ImmutableArray.Create(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), new MockProjectDiagnosticAnalyzer()))); + private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() + => new(ImmutableDictionary>.Empty.Add(LanguageNames.CSharp, ImmutableArray.Create( + DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), + new MockProjectDiagnosticAnalyzer()))); [DiagnosticAnalyzer(LanguageNames.CSharp), PartNotDiscoverable] private class MockProjectDiagnosticAnalyzer : DiagnosticAnalyzer diff --git a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs index 491ced7fb8b9e..5c1b0ab8722fe 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs @@ -28,7 +28,7 @@ public async Task LinkedDocuments_AllTracked() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var caretLocation = testLspServer.GetLocations("caret").Single(); await DidOpen(testLspServer, caretLocation.Uri); @@ -69,7 +69,7 @@ void M() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var caretLocation = testLspServer.GetLocations("caret").Single(); var updatedText = diff --git a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs index 0e502f913103b..daaab4638e958 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs @@ -30,7 +30,7 @@ void M2() var (testLspServer, locationTyped, _) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { Assert.Empty(testLspServer.GetTrackedTexts()); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs index 64deb3cba6982..6a11dd62e93db 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs @@ -12,12 +12,17 @@ using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.DocumentChanges { public partial class DocumentChangesTests : AbstractLanguageServerProtocolTests { + public DocumentChangesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task DocumentChanges_EndToEnd() { @@ -39,7 +44,7 @@ void M() }"; var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { Assert.Empty(testLspServer.GetTrackedTexts()); @@ -74,7 +79,7 @@ void M() }"; var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -98,7 +103,7 @@ void M() }"; var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -119,7 +124,7 @@ void M() }"; var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await Assert.ThrowsAsync(() => DidClose(testLspServer, locationTyped.Uri)); } @@ -138,7 +143,7 @@ void M() }"; var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await Assert.ThrowsAsync(() => DidChange(testLspServer, locationTyped.Uri, (0, 0, "goo"))); } @@ -158,7 +163,7 @@ void M() var (testLspServer, locationTyped, _) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -190,7 +195,7 @@ void M() var (testLspServer, locationTyped, _) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -225,7 +230,7 @@ void M() var (testLspServer, locationTyped, documentText) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -264,7 +269,7 @@ void M() var (testLspServer, locationTyped, _) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); @@ -300,7 +305,7 @@ void M() var (testLspServer, locationTyped, _) = await GetTestLspServerAndLocationAsync(source); - using (testLspServer) + await using (testLspServer) { await DidOpen(testLspServer, locationTyped.Uri); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs index ed1f6c78e6f0c..64ab4b4fe8715 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs @@ -10,19 +10,24 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.FoldingRanges { public class FoldingRangesTests : AbstractLanguageServerProtocolTests { + public FoldingRangesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetFoldingRangeAsync_Imports() { var markup = @"using {|foldingRange:System; using System.Linq;|}"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = testLspServer.GetLocations("foldingRange") .Select(location => CreateFoldingRange(LSP.FoldingRangeKind.Imports, location.Range, "...")) .ToArray(); @@ -38,7 +43,7 @@ public async Task TestGetFoldingRangeAsync_Comments() @"{|foldingRange:// A comment|} {|foldingRange:/* A multiline comment */|}"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = testLspServer.GetLocations("foldingRange") .Select(location => CreateFoldingRange(LSP.FoldingRangeKind.Comment, location.Range, "")) .ToArray(); @@ -54,7 +59,7 @@ public async Task TestGetFoldingRangeAsync_Regions() @"{|foldingRange:#region ARegion #endregion|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = testLspServer.GetLocations("foldingRange") .Select(location => CreateFoldingRange(LSP.FoldingRangeKind.Region, location.Range, "ARegion")) .ToArray(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs index 20f9e9461f55f..2ffb9c1b0fd84 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs @@ -9,12 +9,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Formatting { public class FormatDocumentOnTypeTests : AbstractLanguageServerProtocolTests { + public FormatDocumentOnTypeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestFormatDocumentOnTypeAsync() { @@ -36,7 +41,7 @@ void M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var characterTyped = ";"; var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single().GetTextAsync(); @@ -67,7 +72,7 @@ void M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var characterTyped = ";"; var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single().GetTextAsync(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs index 514f4504ee7ff..1a81986a12f0e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs @@ -9,12 +9,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Formatting { public class FormatDocumentRangeTests : AbstractLanguageServerProtocolTests { + public FormatDocumentRangeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestFormatDocumentRangeAsync() { @@ -34,7 +39,7 @@ void M() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var rangeToFormat = testLspServer.GetLocations("format").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(rangeToFormat.Uri).Single().GetTextAsync(); @@ -62,7 +67,7 @@ void M() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var rangeToFormat = testLspServer.GetLocations("format").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(rangeToFormat.Uri).Single().GetTextAsync(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs index 1f41b900316c2..137c9c8036a41 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs @@ -10,12 +10,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Formatting { public class FormatDocumentTests : AbstractLanguageServerProtocolTests { + public FormatDocumentTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestFormatDocumentAsync() { @@ -35,7 +40,7 @@ void M() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentURI = testLspServer.GetLocations("caret").Single().Uri; var documentText = await testLspServer.GetCurrentSolution().GetDocuments(documentURI).Single().GetTextAsync(); @@ -63,7 +68,7 @@ void M() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentURI = testLspServer.GetLocations("caret").Single().Uri; var documentText = await testLspServer.GetCurrentSolution().GetDocuments(documentURI).Single().GetTextAsync(); @@ -91,7 +96,7 @@ void M() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentURI = testLspServer.GetLocations("caret").Single().Uri; var documentText = await testLspServer.GetCurrentSolution().GetDocuments(documentURI).Single().GetTextAsync(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs index 836e1722acc66..021d458005957 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs @@ -10,12 +10,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Highlights { public class DocumentHighlightTests : AbstractLanguageServerProtocolTests { + public DocumentHighlightTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetDocumentHighlightAsync() { @@ -32,7 +37,7 @@ void M() {|caret:|}{|write:classB|} = new B(); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.DocumentHighlight[] { CreateDocumentHighlight(LSP.DocumentHighlightKind.Text, testLspServer.GetLocations("text").Single()), @@ -57,7 +62,7 @@ class A {|caret:|}{|text:await|} Task.Delay(100); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expectedLocations = testLspServer.GetLocations("text"); @@ -81,7 +86,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGetDocumentHighlightAsync(testLspServer, testLspServer.GetLocations("caret").Single()); Assert.Empty(results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs index 53e371d2ed53c..9cd0837dbd6e1 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs @@ -11,12 +11,17 @@ using Microsoft.VisualStudio.Text.Adornments; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Hover { public class HoverTests : AbstractLanguageServerProtocolTests { + public HoverTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetHoverAsync() { @@ -32,7 +37,7 @@ public async Task TestGetHoverAsync() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -56,7 +61,7 @@ public async Task TestGetHoverAsync_WithExceptions() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -79,7 +84,7 @@ public async Task TestGetHoverAsync_WithRemarks() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -107,7 +112,7 @@ public async Task TestGetHoverAsync_WithList() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -130,7 +135,7 @@ private string Method(int i) {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGetHoverAsync(testLspServer, testLspServer.GetLocations("caret").Single()).ConfigureAwait(false); Assert.Null(results); @@ -179,7 +184,7 @@ static void Main(string[] args) "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, initializationOptions: new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions }); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, initializationOptions: new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions }); var location = testLspServer.GetLocations("caret").Single(); foreach (var project in testLspServer.GetCurrentSolution().Projects) @@ -233,7 +238,7 @@ public async Task TestGetHoverAsync_UsingMarkupContent() { TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expectedLocation = testLspServer.GetLocations("caret").Single(); var expectedMarkdown = @$"```csharp @@ -303,7 +308,7 @@ public async Task TestGetHoverAsync_WithoutMarkdownClientSupport() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expectedLocation = testLspServer.GetLocations("caret").Single(); var expectedText = @$"void A.AMethod(int i) @@ -363,7 +368,7 @@ public async Task TestGetHoverAsync_UsingMarkupContentProperlyEscapes() { TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expectedLocation = testLspServer.GetLocations("caret").Single(); var expectedMarkdown = @"```csharp diff --git a/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs index 890bdea8315e8..5b3578f7b2a0f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs @@ -10,12 +10,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public class InlineCompletionsTests : AbstractLanguageServerProtocolTests { + public InlineCompletionsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + protected override TestComposition Composition => base.Composition .AddParts(typeof(TestSnippetInfoService)); @@ -240,7 +245,7 @@ void M() $0 }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var locationTyped = testLspServer.GetLocations("tab").Single(); var document = testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single(); @@ -265,7 +270,7 @@ void M() private async Task VerifyMarkupAndExpected(string markup, string expected, LSP.FormattingOptions? options = null) { - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var locationTyped = testLspServer.GetLocations("tab").Single(); var document = testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs index ad734e817ed6a..2116499f1d230 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs @@ -11,33 +11,39 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests { [UseExportProvider] public class LanguageServerTargetTests : AbstractLanguageServerProtocolTests { + public LanguageServerTargetTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + protected override TestComposition Composition => base.Composition.AddParts(typeof(StatefulLspServiceFactory), typeof(StatelessLspService)); [Fact] public async Task LanguageServerQueueEmptyOnShutdownMessage() { - var server = await CreateTestLspServerAsync(""); + await using var server = await CreateTestLspServerAsync(""); AssertServerAlive(server); - server.GetServerAccessor().ShutdownServer(); + await server.ShutdownTestServerAsync(); await AssertServerQueueClosed(server).ConfigureAwait(false); Assert.False(server.GetServerAccessor().GetServerRpc().IsDisposed); + await server.ExitTestServerAsync(); } [Fact] public async Task LanguageServerCleansUpOnExitMessage() { - var server = await CreateTestLspServerAsync(""); + await using var server = await CreateTestLspServerAsync(""); AssertServerAlive(server); - server.GetServerAccessor().ShutdownServer(); - server.GetServerAccessor().ExitServer(); + await server.ShutdownTestServerAsync(); + await server.ExitTestServerAsync(); await AssertServerQueueClosed(server).ConfigureAwait(false); Assert.True(server.GetServerAccessor().GetServerRpc().IsDisposed); } @@ -45,7 +51,7 @@ public async Task LanguageServerCleansUpOnExitMessage() [Fact] public async Task LanguageServerCleansUpOnUnexpectedJsonRpcDisconnectAsync() { - using var server = await CreateTestLspServerAsync(""); + await using var server = await CreateTestLspServerAsync(""); AssertServerAlive(server); server.GetServerAccessor().GetServerRpc().Dispose(); @@ -56,8 +62,8 @@ public async Task LanguageServerCleansUpOnUnexpectedJsonRpcDisconnectAsync() [Fact] public async Task LanguageServerHasSeparateServiceInstances() { - using var serverOne = await CreateTestLspServerAsync(""); - using var serverTwo = await CreateTestLspServerAsync(""); + await using var serverOne = await CreateTestLspServerAsync(""); + await using var serverTwo = await CreateTestLspServerAsync(""); // Get an LSP service and verify each server has its own instance per server. Assert.NotSame(serverOne.GetRequiredLspService(), serverTwo.GetRequiredLspService()); @@ -71,7 +77,7 @@ public async Task LanguageServerHasSeparateServiceInstances() [Fact] public async Task LanguageServerDisposesOfServicesOnShutdown() { - using var server = await CreateTestLspServerAsync(""); + await using var server = await CreateTestLspServerAsync(""); var statefulService = server.GetRequiredLspService(); var statelessService = server.GetRequiredLspService(); @@ -79,8 +85,8 @@ public async Task LanguageServerDisposesOfServicesOnShutdown() Assert.False(statefulService.IsDisposed); Assert.False(statelessService.IsDisposed); - server.GetServerAccessor().ShutdownServer(); - server.GetServerAccessor().ExitServer(); + await server.ShutdownTestServerAsync(); + await server.ExitTestServerAsync(); // Only the stateful service should be disposed of on server shutdown. Assert.True(statefulService.IsDisposed); @@ -90,14 +96,15 @@ public async Task LanguageServerDisposesOfServicesOnShutdown() private static void AssertServerAlive(TestLspServer server) { Assert.False(server.GetServerAccessor().HasShutdownStarted()); - Assert.False(server.GetQueueAccessor().IsComplete()); + Assert.False(server.GetQueueAccessor()!.Value.IsComplete()); } private static async Task AssertServerQueueClosed(TestLspServer server) { - await server.GetQueueAccessor().WaitForProcessingToStopAsync().ConfigureAwait(false); + var queueAccessor = server.GetQueueAccessor()!.Value; + await queueAccessor.WaitForProcessingToStopAsync().ConfigureAwait(false); Assert.True(server.GetServerAccessor().HasShutdownStarted()); - Assert.True(server.GetQueueAccessor().IsComplete()); + Assert.True(queueAccessor.IsComplete()); } [ExportCSharpVisualBasicLspServiceFactory(typeof(StatefulLspService)), Shared] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index 36daed66d0ee4..a1d04434f5a47 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -11,12 +11,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Miscellaneous; public class LspMiscellaneousFilesWorkspaceTests : AbstractLanguageServerProtocolTests { + public LspMiscellaneousFilesWorkspaceTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestLooseFile_Opened() { @@ -29,7 +34,7 @@ void M() }"; // Create a server that supports LSP misc files and verify no misc files present. - using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); Assert.Null(GetMiscellaneousDocument(testLspServer)); // Open an empty loose file and make a request to verify it gets added to the misc workspace. @@ -52,7 +57,11 @@ void M() }"; // Create a server that supports LSP misc files and verify no misc files present. - using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + + var miscWorkspace = testLspServer.GetRequiredLspService(); + testLspServer.TestWorkspace.GetService().Register(miscWorkspace); + Assert.Null(GetMiscellaneousDocument(testLspServer)); var looseFileUri = new Uri(@"C:\SomeFile.cs"); @@ -81,7 +90,7 @@ void M() }"; // Create a server that supports LSP misc files and verify no misc files present. - using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); Assert.Null(GetMiscellaneousDocument(testLspServer)); // Open an empty loose file and make a request to verify it gets added to the misc workspace. @@ -106,7 +115,7 @@ void M() }"; // Create a server that supports LSP misc files and verify no misc files present. - using var testLspServer = await CreateTestLspServerAsync(markup, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(markup, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); Assert.Null(GetMiscellaneousDocument(testLspServer)); // Open a file that is part of a registered workspace and verify it is not present in the misc workspace. @@ -127,7 +136,7 @@ void M() }"; // Create a server that supports LSP misc files and verify no misc files present. - using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); Assert.Null(GetMiscellaneousDocument(testLspServer)); // Open an empty loose file and make a request to verify it gets added to the misc workspace. @@ -167,7 +176,7 @@ void M() }"; // Create a server that doesn't use the LSP misc files workspace. - using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, new InitializationOptions { ServerKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer }); Assert.Null(testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()); // Open an empty loose file and make a request to verify it gets added to the misc workspace. diff --git a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index f6b24d149b2d5..4614c6a570e91 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs @@ -10,6 +10,7 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.OnAutoInsert @@ -17,6 +18,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.OnAutoInsert [Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] public class OnAutoInsertTests : AbstractLanguageServerProtocolTests { + public OnAutoInsertTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task OnAutoInsert_CommentCharacter() { @@ -395,7 +400,7 @@ private async Task VerifyMarkupAndExpected( throw ExceptionUtilities.UnexpectedValue(languageName); } - using var testLspServer = await testLspServerTask; + await using var testLspServer = await testLspServerTask; var locationTyped = testLspServer.GetLocations("type").Single(); var document = testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single(); @@ -411,7 +416,7 @@ private async Task VerifyMarkupAndExpected( private async Task VerifyNoResult(string characterTyped, string markup, bool insertSpaces = true, int tabSize = 4) { - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single().GetTextAsync(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs index 3c1534b58ca63..1968051ca3771 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs @@ -1,7 +1,6 @@ // 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.Composition; @@ -15,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(FailingMutatingRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class FailingMutatingRequestHandler : IRequestHandler + internal class FailingMutatingRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(FailingMutatingRequestHandler); private const int Delay = 100; @@ -29,8 +28,6 @@ public FailingMutatingRequestHandler() public bool MutatesSolutionState => true; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { await Task.Delay(Delay, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs index fbfe182553cee..3ae1219f938d7 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Composition; using System.Threading; @@ -16,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(FailingRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class FailingRequestHandler : IRequestHandler + internal class FailingRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(FailingRequestHandler); private const int Delay = 100; @@ -30,8 +28,6 @@ public FailingRequestHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { await Task.Delay(Delay, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs index ce693dc292e61..0ef3ce29f1c11 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Composition; using System.Threading; @@ -17,7 +15,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(LongRunningNonMutatingRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class LongRunningNonMutatingRequestHandler : IRequestHandler + internal class LongRunningNonMutatingRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(LongRunningNonMutatingRequestHandler); @@ -31,8 +29,6 @@ public LongRunningNonMutatingRequestHandler() public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { do diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs index 8df05116b94e4..1492eddbdd29c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs @@ -1,7 +1,6 @@ // 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.Composition; @@ -15,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(MutatingRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class MutatingRequestHandler : IRequestHandler + internal class MutatingRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(MutatingRequestHandler); private const int Delay = 100; @@ -29,8 +28,6 @@ public MutatingRequestHandler() public bool MutatesSolutionState => true; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { var response = new TestResponse diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs index 498d899349f17..91ce841d967fd 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Composition; using System.Threading; @@ -17,7 +15,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(NonLSPSolutionRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class NonLSPSolutionRequestHandler : IRequestHandler + internal class NonLSPSolutionRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(NonLSPSolutionRequestHandler); @@ -30,8 +28,6 @@ public NonLSPSolutionRequestHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => false; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { Assert.Null(context.Solution); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs index 5b9c35abbba2d..90167e66d8b6c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs @@ -1,7 +1,6 @@ // 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.Composition; @@ -15,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { [ExportCSharpVisualBasicStatelessLspService(typeof(NonMutatingRequestHandler)), PartNotDiscoverable, Shared] [Method(MethodName)] - internal class NonMutatingRequestHandler : IRequestHandler + internal class NonMutatingRequestHandler : ILspServiceRequestHandler { public const string MethodName = nameof(NonMutatingRequestHandler); private const int Delay = 100; @@ -29,8 +28,6 @@ public NonMutatingRequestHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { var response = new TestResponse(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs index d6a02dbaa9006..4db0bb46b2269 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs @@ -7,18 +7,22 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { public partial class RequestOrderingTests : AbstractLanguageServerProtocolTests { + public RequestOrderingTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + protected override TestComposition Composition => base.Composition .AddParts(typeof(MutatingRequestHandler)) .AddParts(typeof(NonMutatingRequestHandler)) @@ -36,7 +40,7 @@ public async Task MutatingRequestsDontOverlap() new TestRequest(MutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var responses = await TestAsync(testLspServer, requests); // Every request should have started at or after the one before it @@ -53,7 +57,7 @@ public async Task NonMutatingRequestsOverlap() new TestRequest(NonMutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var responses = await TestAsync(testLspServer, requests); // Every request should have started immediately, without waiting @@ -70,7 +74,7 @@ public async Task NonMutatingWaitsForMutating() new TestRequest(NonMutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var responses = await TestAsync(testLspServer, requests); // The non mutating tasks should have waited for the first task to finish @@ -90,7 +94,7 @@ public async Task MutatingDoesntWaitForNonMutating() new TestRequest(MutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var responses = await TestAsync(testLspServer, requests); // All tasks should start without waiting for any to finish @@ -110,7 +114,7 @@ public async Task ThrowingTaskDoesntBringDownQueue() new TestRequest(NonMutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var waitables = StartTestRun(testLspServer, requests); // first task should fail @@ -133,7 +137,7 @@ public async Task LongRunningSynchronousNonMutatingTaskDoesNotBlockQueue() new TestRequest(NonMutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); // Cancel all requests if the request queue is blocked for 1 minute. This will result in a failed test run. using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); @@ -165,7 +169,7 @@ public async Task FailingMutableTaskShutsDownQueue() new TestRequest(NonMutatingRequestHandler.MethodName), }; - using var testLspServer = await CreateTestLspServerAsync("class C { }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { }"); var waitables = StartTestRun(testLspServer, requests); // first task should fail @@ -173,17 +177,17 @@ public async Task FailingMutableTaskShutsDownQueue() // The failed request returns to the client before the shutdown completes. // Wait for the queue to finish handling the failed request and shutdown. - await testLspServer.GetQueueAccessor().WaitForProcessingToStopAsync().ConfigureAwait(false); + await testLspServer.GetQueueAccessor()!.Value.WaitForProcessingToStopAsync().ConfigureAwait(false); // remaining tasks should be canceled - var areAllItemsCancelled = await testLspServer.GetQueueAccessor().AreAllItemsCancelledUnsafeAsync(); + var areAllItemsCancelled = await testLspServer.GetQueueAccessor()!.Value.AreAllItemsCancelledUnsafeAsync(); Assert.True(areAllItemsCancelled); } [Fact] public async Task NonMutatingRequestsOperateOnTheSameSolutionAfterMutation() { - using var testLspServer = await CreateTestLspServerAsync("class C { {|caret:|} }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { {|caret:|} }"); var expectedSolution = testLspServer.GetCurrentSolution(); @@ -223,7 +227,7 @@ public async Task NonMutatingRequestsOperateOnTheSameSolutionAfterMutation() [Fact] public async Task HandlerThatSkipsBuildingLSPSolutionGetsWorkspaceSolution() { - using var testLspServer = await CreateTestLspServerAsync("class C { {|caret:|} }"); + await using var testLspServer = await CreateTestLspServerAsync("class C { {|caret:|} }"); var solution = await GetLSPSolution(testLspServer, NonLSPSolutionRequestHandler.MethodName); Assert.Null(solution); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs index 60aa9c11ba000..56ef5d149805b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs @@ -8,12 +8,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.ProjectContext { public class GetTextDocumentWithContextHandlerTests : AbstractLanguageServerProtocolTests { + public GetTextDocumentWithContextHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task SingleDocumentReturnsSingleContext() { @@ -24,7 +29,7 @@ public async Task SingleDocumentReturnsSingleContext() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var documentUri = testLspServer.GetLocations("caret").Single().Uri; var result = await RunGetProjectContext(testLspServer, documentUri); @@ -50,7 +55,7 @@ public async Task MultipleDocumentsReturnsMultipleContexts() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var documentUri = testLspServer.GetLocations("caret").Single().Uri; var result = await RunGetProjectContext(testLspServer, documentUri); @@ -74,7 +79,7 @@ public async Task SwitchingContextsChangesDefaultContext() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); // Ensure the documents are open so we can change contexts foreach (var document in testLspServer.TestWorkspace.Documents) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 71cf34fe4ba58..aa4e29ce808f2 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -16,12 +16,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.References { public class FindAllReferencesHandlerTests : AbstractLanguageServerProtocolTests { + public FindAllReferencesHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [WpfFact] public async Task TestFindAllReferencesAsync() { @@ -42,7 +47,7 @@ void M2() var j = someInt + A.{|caret:|}{|reference:someInt|}; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -78,7 +83,7 @@ void M2() var j = someInt + A.{|caret:|}{|reference:someInt|}; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); using var progress = BufferedProgress.Create(null); @@ -128,7 +133,7 @@ void M2() var j = someInt + {|caret:|}{|reference:A|}.someInt; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -171,7 +176,7 @@ void M2() }" }; - using var testLspServer = await CreateTestLspServerAsync(markups, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions }); + await using var testLspServer = await CreateTestLspServerAsync(markups, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions }); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -195,7 +200,7 @@ public async Task TestFindAllReferencesAsync_InvalidLocation() { {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); Assert.Empty(results); @@ -214,7 +219,7 @@ void M() Console.{|caret:|}{|reference:WriteLine|}(""text""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); Assert.NotNull(results[0].Location.Uri); @@ -236,7 +241,7 @@ void M() } } "; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); @@ -266,7 +271,7 @@ void M() } } "; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertHighlightCount(results, expectedDefinitionCount: 1, expectedWrittenReferenceCount: 1, expectedReferenceCount: 1); @@ -278,7 +283,7 @@ public async Task TestFindAllReferencesAsync_StaticClassification() var markup = @"static class {|caret:|}{|reference:C|} { } "; - using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs index 0f145ac8beb1f..414731260b0d8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs @@ -11,12 +11,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.References { public class FindImplementationsTests : AbstractLanguageServerProtocolTests { + public FindImplementationsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestFindImplementationAsync() { @@ -31,7 +36,7 @@ class A : IA { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunFindImplementationAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("implementation"), results); @@ -60,7 +65,7 @@ class A : IA }" }; - using var testLspServer = await CreateTestLspServerAsync(markups); + await using var testLspServer = await CreateTestLspServerAsync(markups); var results = await RunFindImplementationAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("implementation"), results); @@ -80,7 +85,7 @@ void IA.M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(string.Empty); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty); AddMappedDocument(testLspServer.TestWorkspace, markup); @@ -104,7 +109,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunFindImplementationAsync(testLspServer, testLspServer.GetLocations("caret").Single()); Assert.Empty(results); @@ -119,7 +124,7 @@ public async Task TestFindImplementationAsync_MultipleLocations() class {|implementation:B|} : A { } class {|implementation:C|} : A { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunFindImplementationAsync(testLspServer, testLspServer.GetLocations("caret").Single()); AssertLocationsEqual(testLspServer.GetLocations("implementation"), results); @@ -138,7 +143,7 @@ class C : IDisposable d.{|caret:|}Dispose(); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunFindImplementationAsync(testLspServer, testLspServer.GetLocations("caret").Single()); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs index ca7e7204ca120..aee25a9a92339 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs @@ -12,12 +12,17 @@ using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Rename { public class RenameTests : AbstractLanguageServerProtocolTests { + public RenameTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [WpfFact] public async Task TestRenameAsync() { @@ -32,7 +37,7 @@ void M2() {|renamed:M|}() } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var renameLocation = testLspServer.GetLocations("caret").First(); var renameValue = "RENAME"; var expectedEdits = testLspServer.GetLocations("renamed").Select(location => new LSP.TextEdit() { NewText = renameValue, Range = location.Range }); @@ -55,7 +60,7 @@ void M2() {|renamed:M|}() } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var renameLocation = testLspServer.GetLocations("caret").First(); var renameValue = "$RENAMED$"; @@ -88,7 +93,7 @@ void M2() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var renameLocation = testLspServer.GetLocations("caret").First(); var renameValue = "RENAME"; var expectedEdits = testLspServer.GetLocations("renamed").Select(location => new LSP.TextEdit() { NewText = renameValue, Range = location.Range }); @@ -134,7 +139,7 @@ void M4() "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var renameLocation = testLspServer.GetLocations("caret").First(); var renameValue = "RENAME"; var expectedEdits = testLspServer.GetLocations("renamed").Select(location => new LSP.TextEdit() { NewText = renameValue, Range = location.Range }); @@ -157,7 +162,7 @@ void M2() M() } }"; - using var testLspServer = await CreateTestLspServerAsync(string.Empty); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty); AddMappedDocument(testLspServer.TestWorkspace, markup); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 61b3ffbd42605..99de92dcb75f9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -13,12 +13,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens { public abstract class AbstractSemanticTokensTests : AbstractLanguageServerProtocolTests { + protected AbstractSemanticTokensTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + private protected static async Task RunGetSemanticTokensRangeAsync(TestLspServer testLspServer, LSP.Location caret, LSP.Range range) { var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensRangeName, diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs index fbefedaf942c1..e9dc3adaa38df 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs @@ -15,12 +15,17 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; using Microsoft.VisualStudio.LanguageServer.Protocol; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens { public class SemanticTokensRangeTests : AbstractSemanticTokensTests { + public SemanticTokensRangeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetSemanticTokensRange_FullDocAsync() { @@ -28,7 +33,7 @@ public async Task TestGetSemanticTokensRange_FullDocAsync() @"{|caret:|}// Comment static class C { } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); @@ -50,7 +55,7 @@ public async Task TestGetSemanticTokensRange_FullDoc_RazorAsync() @"{|caret:|}// Comment static class C { } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; @@ -85,7 +90,7 @@ public async Task TestGetSemanticTokensRange_PartialDoc_RazorAsync() @"{|caret:|}// Comment static class C { } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(1, 0), End = new Position(2, 0) }; @@ -120,7 +125,7 @@ public async Task TestGetSemanticTokensRange_MultiLineComment_RazorAsync() two three */ } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(4, 0) }; @@ -162,7 +167,7 @@ void M() } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); @@ -195,7 +200,7 @@ void M() } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; @@ -248,7 +253,7 @@ void M() } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs index b3262e025cc07..17d9cd1795a03 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs @@ -9,12 +9,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SignatureHelp { public class SignatureHelpTests : AbstractLanguageServerProtocolTests { + public SignatureHelpTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetSignatureHelpAsync() { @@ -34,7 +39,7 @@ int M2(string a) } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SignatureHelp() { ActiveParameter = 0, diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs index c1af4cd7f94cb..f7bebf07ba29d 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs @@ -14,11 +14,15 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SpellCheck { public class SpellCheckTests : AbstractLanguageServerProtocolTests { + public SpellCheckTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } #region Document [Fact] @@ -28,7 +32,7 @@ public async Task TestNoDocumentResultsForClosedFiles() @"class A { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); var results = await RunGetDocumentSpellCheckSpansAsync(testLspServer, document.GetURI()); @@ -43,7 +47,7 @@ public async Task TestDocumentResultsForOpenFiles() @"class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. var testDocument = testLspServer.TestWorkspace.Documents.Single(); @@ -71,7 +75,7 @@ public async Task TestDocumentResultsForRemovedDocument() @"class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var workspace = testLspServer.TestWorkspace; // Calling GetTextBuffer will effectively open the file. @@ -111,7 +115,7 @@ public async Task TestNoChangeIfDocumentResultsCalledTwice() @"class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -147,7 +151,7 @@ public async Task TestDocumentResultChangedAfterEntityAdded() } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -195,7 +199,7 @@ public async Task TestDocumentResultIdChangesAfterEdit() @"class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -236,7 +240,7 @@ public async Task TestDocumentResultsAreNotMapped() class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -263,7 +267,7 @@ public async Task TestStreamingDocumentDiagnostics() @"class {|Identifier:A|} { }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -295,7 +299,7 @@ public async Task TestWorkspaceResultsForClosedFiles() { }"; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); + await using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); @@ -329,7 +333,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); @@ -342,7 +346,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec // var markup1 = //@"class A {"; // var markup2 = ""; - // using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + // await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( // markups: Array.Empty(), // sourceGeneratedMarkups: new[] { markup1, markup2 }, // BackgroundAnalysisScope.FullSolution, @@ -368,7 +372,7 @@ public async Task TestWorkspaceResultsForRemovedDocument() { }"; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); + await using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); @@ -406,7 +410,7 @@ public async Task TestNoChangeIfWorkspaceResultsCalledTwice() { }"; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); + await using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); @@ -442,7 +446,7 @@ public async Task TestWorkspaceResultUpdatedAfterEdit() "; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); + await using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); @@ -495,7 +499,7 @@ public async Task TestStreamingWorkspaceResults() { }"; var markup2 = ""; - using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); + await using var testLspServer = await CreateTestLspServerAsync(new[] { markup1, markup2 }); var results = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs index 87a3d8e18b118..c60f4fc62d401 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs @@ -10,12 +10,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Symbols { public class DocumentSymbolsTests : AbstractLanguageServerProtocolTests { + public DocumentSymbolsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestGetDocumentSymbolsAsync() { @@ -36,7 +41,7 @@ public async Task TestGetDocumentSymbolsAsync() } } }; - using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + await using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expected = new LSP.DocumentSymbol[] { CreateDocumentSymbol(LSP.SymbolKind.Class, "A", "A", testLspServer.GetLocations("class").Single(), testLspServer.GetLocations("classSelection").Single()) @@ -57,7 +62,7 @@ public async Task TestGetDocumentSymbolsAsync__WithoutHierarchicalSupport() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Class, "A", testLspServer.GetLocations("class").Single(), Glyph.ClassInternal), @@ -81,7 +86,7 @@ void Method() int i = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGetDocumentSymbolsAsync(testLspServer).ConfigureAwait(false); Assert.Equal(3, results.Length); } @@ -89,7 +94,7 @@ void Method() [Fact] public async Task TestGetDocumentSymbolsAsync__NoSymbols() { - using var testLspServer = await CreateTestLspServerAsync(string.Empty); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty); var results = await RunGetDocumentSymbolsAsync(testLspServer); Assert.Empty(results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs index 62ab70aadee6a..63461f4e8b860 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs @@ -11,12 +11,17 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Symbols { public class WorkspaceSymbolsTests : AbstractLanguageServerProtocolTests { + public WorkspaceSymbolsTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + private static void AssertSetEquals(LSP.SymbolInformation[] expected, LSP.SymbolInformation[]? results) => Assert.True(expected.ToHashSet().SetEquals(results)); @@ -30,7 +35,7 @@ void M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Class, "A", testLspServer.GetLocations("class").Single(), Glyph.ClassInternal, GetContainerName(testLspServer.GetCurrentSolution())) @@ -50,7 +55,7 @@ void M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Class, "A", testLspServer.GetLocations("class").Single(), Glyph.ClassInternal, GetContainerName(testLspServer.GetCurrentSolution())) @@ -76,7 +81,7 @@ public async Task TestGetWorkspaceSymbolsAsync_Method() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Method, "M", testLspServer.GetLocations("method").Single(), Glyph.MethodPrivate, GetContainerName(testLspServer.GetCurrentSolution(), "A")) @@ -99,7 +104,7 @@ void M() int {|local:i|} = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Variable, "i", testLspServer.GetLocations("local").Single(), Glyph.Local, GetContainerName(testLspServer.GetCurrentSolution(), "A.M.i")) @@ -124,7 +129,7 @@ class {|class:F|} int {|field:F|}; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var classAContainerName = GetContainerName(testLspServer.GetCurrentSolution(), "A"); var expected = new LSP.SymbolInformation[] { @@ -156,7 +161,7 @@ public async Task TestGetWorkspaceSymbolsAsync_MultipleDocuments() }" }; - using var testLspServer = await CreateTestLspServerAsync(markups); + await using var testLspServer = await CreateTestLspServerAsync(markups); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Method, "M", testLspServer.GetLocations("method")[0], Glyph.MethodPrivate, GetContainerName(testLspServer.GetCurrentSolution(), "A")), @@ -177,7 +182,7 @@ void M() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var results = await RunGetWorkspaceSymbolsAsync(testLspServer, "NonExistingSymbol").ConfigureAwait(false); Assert.Empty(results); @@ -191,7 +196,7 @@ Sub Method() End Sub End Class"; - using var testLspServer = await CreateVisualBasicTestLspServerAsync(markup); + await using var testLspServer = await CreateVisualBasicTestLspServerAsync(markup); var expected = new LSP.SymbolInformation[] { CreateSymbolInformation(LSP.SymbolKind.Class, "A", testLspServer.GetLocations("class").Single(), Glyph.ClassInternal, GetContainerName(testLspServer.GetCurrentSolution())) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs index 3e7b5e7eecf6b..be0e8c22d3967 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs @@ -3,33 +3,35 @@ // 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.IO; using System.Linq; using System.ServiceModel.Syndication; -using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServer.Protocol; using Nerdbank.Streams; using Roslyn.Test.Utilities; using StreamJsonRpc; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public class VSTypeScriptHandlerTests : AbstractLanguageServerProtocolTests { + public VSTypeScriptHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + protected override TestComposition Composition => base.Composition.AddParts(typeof(TypeScriptHandlerFactory)); [Fact] @@ -42,7 +44,7 @@ public async Task TestExternalAccessTypeScriptHandlerInvoked() "; - using var testLspServer = await CreateTsTestLspServerAsync(workspaceXml); + await using var testLspServer = await CreateTsTestLspServerAsync(workspaceXml); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); var request = new TSRequest(document.GetURI(), ProtocolConversions.ProjectIdToProjectContextId(document.Project.Id)); @@ -61,8 +63,12 @@ public async Task TestRoslynTypeScriptHandlerInvoked() "; - using var testLspServer = await CreateTsTestLspServerAsync(workspaceXml); - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(InternalDiagnosticsOptions.NormalDiagnosticMode), DiagnosticMode.Pull); + var options = new InitializationOptions() + { + OptionUpdater = globalOptions => globalOptions.SetGlobalOption(new OptionKey(InternalDiagnosticsOptions.NormalDiagnosticMode), DiagnosticMode.Pull) + }; + + await using var testLspServer = await CreateTsTestLspServerAsync(workspaceXml, options); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); var documentPullRequest = new VSInternalDocumentDiagnosticsParams @@ -74,10 +80,12 @@ public async Task TestRoslynTypeScriptHandlerInvoked() Assert.Empty(response); } - private async Task CreateTsTestLspServerAsync(string workspaceXml) + private async Task CreateTsTestLspServerAsync(string workspaceXml, InitializationOptions? options = null) { var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - var testWorkspace = TestWorkspace.Create(workspaceXml, composition: Composition); + + var testWorkspace = CreateWorkspace(options, workspaceKind: null); + testWorkspace.InitializeDocuments(XElement.Parse(workspaceXml), openDocuments: false); // Ensure workspace operations are completed so we don't get unexpected workspace changes while running. await WaitForWorkspaceOperationsAsync(testWorkspace); @@ -86,9 +94,8 @@ private async Task CreateTsTestLspServerAsync(string workspaceXml return await TestLspServer.CreateAsync(testWorkspace, new ClientCapabilities(), languageServerTarget, clientStream); } - private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace) + private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace) { - var listenerProvider = workspace.ExportProvider.GetExportedValue(); var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var servicesProvider = workspace.ExportProvider.GetExportedValue(); @@ -97,11 +104,12 @@ private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Str ExceptionStrategy = ExceptionProcessing.ISerializable, }; - var languageServer = new LanguageServerTarget( + var logger = NoOpLspLogger.Instance; + + var languageServer = new RoslynLanguageServer( servicesProvider, jsonRpc, capabilitiesProvider, - listenerProvider, - NoOpLspLogger.Instance, + logger, ImmutableArray.Create(InternalLanguageNames.TypeScript), WellKnownLspServerKinds.RoslynTypeScriptLspServer); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs index e156e4cfddafd..c758fb24aca66 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs @@ -9,12 +9,17 @@ using System.Threading.Tasks; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.ValidateBreakableRange { public class ValidateBreakableRange : AbstractLanguageServerProtocolTests { + public ValidateBreakableRange(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task SimpleStatement() { @@ -26,7 +31,7 @@ void M() {|expected:{|caret:|}M();|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caret = testLspServer.GetLocations("caret").Single(); @@ -49,7 +54,7 @@ void M() #endif } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caret = testLspServer.GetLocations("caret").Single(); @@ -70,7 +75,7 @@ void M() {|caret:|}const int a = 1; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var caret = testLspServer.GetLocations("caret").Single(); @@ -89,7 +94,7 @@ void M() {|breakpoint:int a = 1; {|expected:Console.WriteLine(""hello"");|}|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -110,7 +115,7 @@ void M() {|breakpoint:int a Console.WriteLine(""hello"");|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -132,7 +137,7 @@ void M() {|breakpoint:int a = 1;|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -155,7 +160,7 @@ void M() {|expected:Console.WriteLine(""hello"");|}|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -182,7 +187,7 @@ void M() );|}|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -205,7 +210,7 @@ void M() {|breakpoint:GetSomeValue();|}|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); @@ -227,7 +232,7 @@ void M() GetSomeValue();|}|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var breakpoint = testLspServer.GetLocations("breakpoint").Single(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index 708d936447962..2a0baefb34550 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -15,16 +15,21 @@ using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Workspaces; public class LspWorkspaceManagerTests : AbstractLanguageServerProtocolTests { + public LspWorkspaceManagerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestUsesLspTextOnOpenCloseAsync() { var markup = ""; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.First().GetURI(); await testLspServer.OpenDocumentAsync(documentUri, "LSP text"); @@ -51,7 +56,7 @@ public async Task TestLspUsesWorkspaceInstanceOnChangesAsync() { var markupOne = "One"; var markupTwo = "Two"; - using var testLspServer = await CreateTestLspServerAsync(new string[] { markupOne, markupTwo }); + await using var testLspServer = await CreateTestLspServerAsync(new string[] { markupOne, markupTwo }); var firstDocumentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test1")).GetURI(); var secondDocumentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test2")).GetURI(); @@ -61,12 +66,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); @@ -90,7 +95,7 @@ public async Task TestLspHasClosedDocumentChangesAsync() { var markupOne = "One"; var markupTwo = "Two"; - using var testLspServer = await CreateTestLspServerAsync(new string[] { markupOne, markupTwo }); + await using var testLspServer = await CreateTestLspServerAsync(new string[] { markupOne, markupTwo }); var firstDocumentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test1")).GetURI(); var secondDocument = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test2")); @@ -100,7 +105,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); @@ -118,7 +123,7 @@ public async Task TestLspHasClosedDocumentChangesAsync() public async Task TestLspHasProjectChangesAsync() { var markup = "One"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test1")).GetURI(); // Open the document via LSP and verify the initial project name. @@ -142,7 +147,7 @@ public async Task TestLspHasProjectChangesAsync() public async Task TestLspHasProjectChangesWithForkedTextAsync() { var markup = "One"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test1")).GetURI(); // Open the document via LSP with different text from the workspace and verify the initial project name. @@ -166,7 +171,7 @@ public async Task TestLspHasProjectChangesWithForkedTextAsync() public async Task TestLspFindsNewDocumentAsync() { var markup = "One"; - using var testLspServer = await CreateTestLspServerAsync(markup); + await using var testLspServer = await CreateTestLspServerAsync(markup); var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.Single(d => d.FilePath!.Contains("test1")).GetURI(); // Open the document via LSP to create the initial LSP solution. @@ -174,7 +179,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); @@ -191,7 +196,7 @@ public async Task TestLspTransfersDocumentToNewWorkspaceAsync() var markup = "One"; // Create a server that includes the LSP misc files workspace so we can test transfers to and from it. - using var testLspServer = await CreateTestLspServerAsync(markup, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + await using var testLspServer = await CreateTestLspServerAsync(markup, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); // Create a new document, but do not update the workspace solution yet. var newDocumentId = DocumentId.CreateNewId(testLspServer.TestWorkspace.CurrentSolution.ProjectIds[0]); @@ -245,23 +250,21 @@ public async Task TestUsesRegisteredHostWorkspace() "; - using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); // Verify 1 workspace registered to start with. Assert.True(IsWorkspaceRegistered(testLspServer.TestWorkspace, testLspServer)); - var exportProvider = testLspServer.TestWorkspace.ExportProvider; - using var testWorkspaceTwo = TestWorkspace.Create( XElement.Parse(secondWorkspaceXml), workspaceKind: "OtherWorkspaceKind", - exportProvider: exportProvider); + composition: testLspServer.TestWorkspace.Composition); // Wait for workspace creation operations for the second workspace to complete. await WaitForWorkspaceOperationsAsync(testWorkspaceTwo); // Manually register the workspace since the workspace listener does not listen for this workspace kind. - var workspaceRegistrationService = exportProvider.GetExport(); - workspaceRegistrationService.Value.Register(testWorkspaceTwo); + var workspaceRegistrationService = testLspServer.TestWorkspace.GetService(); + workspaceRegistrationService.Register(testWorkspaceTwo); // Verify both workspaces registered. Assert.True(IsWorkspaceRegistered(testLspServer.TestWorkspace, testLspServer)); @@ -283,7 +286,7 @@ public async Task TestWorkspaceRequestFailsWhenHostWorkspaceMissing() "; - using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml, workspaceKind: WorkspaceKind.MiscellaneousFiles); + await using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml, workspaceKind: WorkspaceKind.MiscellaneousFiles); var exportProvider = testLspServer.TestWorkspace.ExportProvider; var workspaceRegistrationService = exportProvider.GetExport(); @@ -314,13 +317,10 @@ public async Task TestLspUpdatesCorrectWorkspaceWithMultipleWorkspacesAsync() "; - using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); - var exportProvider = testLspServer.TestWorkspace.ExportProvider; + await using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); - using var testWorkspaceTwo = TestWorkspace.Create( - XElement.Parse(secondWorkspaceXml), - workspaceKind: WorkspaceKind.MSBuild, - exportProvider: exportProvider); + using var testWorkspaceTwo = CreateWorkspace(options: null, WorkspaceKind.MSBuild); + testWorkspaceTwo.InitializeDocuments(XElement.Parse(secondWorkspaceXml)); // Wait for workspace creation operations to complete for the second workspace. await WaitForWorkspaceOperationsAsync(testWorkspaceTwo); @@ -376,14 +376,10 @@ public async Task TestWorkspaceEventUpdatesCorrectWorkspaceWithMultipleWorkspace "; - using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); + await using var testLspServer = await CreateXmlTestLspServerAsync(firstWorkspaceXml); - var exportProvider = testLspServer.TestWorkspace.ExportProvider; - - using var testWorkspaceTwo = TestWorkspace.Create( - XElement.Parse(secondWorkspaceXml), - workspaceKind: WorkspaceKind.MSBuild, - exportProvider: exportProvider); + using var testWorkspaceTwo = CreateWorkspace(options: null, workspaceKind: WorkspaceKind.MSBuild); + testWorkspaceTwo.InitializeDocuments(XElement.Parse(secondWorkspaceXml)); // Wait for workspace operations to complete for the second workspace. await WaitForWorkspaceOperationsAsync(testWorkspaceTwo); @@ -428,15 +424,16 @@ public async Task TestSeparateWorkspaceManagerPerServerAsync() "; - using var testWorkspace = TestWorkspace.Create(XElement.Parse(workspaceXml), composition: Composition); + using var testWorkspace = CreateWorkspace(options: null, workspaceKind: null); + testWorkspace.InitializeDocuments(XElement.Parse(workspaceXml)); // Wait for workspace creation operations to complete. await WaitForWorkspaceOperationsAsync(testWorkspace); var documentUri = testWorkspace.CurrentSolution.Projects.First().Documents.First().GetURI(); - using var testLspServerOne = await TestLspServer.CreateAsync(testWorkspace, new InitializationOptions()); - using var testLspServerTwo = await TestLspServer.CreateAsync(testWorkspace, new InitializationOptions()); + await using var testLspServerOne = await TestLspServer.CreateAsync(testWorkspace, new InitializationOptions(), TestOutputLspLogger); + await using var testLspServerTwo = await TestLspServer.CreateAsync(testWorkspace, new InitializationOptions(), TestOutputLspLogger); Assert.NotEqual(testLspServerOne.GetManager(), testLspServerTwo.GetManager()); @@ -465,6 +462,25 @@ public async Task TestSeparateWorkspaceManagerPerServerAsync() Assert.Equal(newAssemblyName, documentServerTwo.Project.AssemblyName); } + [Fact] + public async Task TestDoesNotForkWhenDocumentTextBufferOpenedAsync() + { + var markup = "Text"; + await using var testLspServer = await CreateTestLspServerAsync(markup); + var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.First().GetURI(); + + // Calling get text buffer opens the document in the workspace. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + + await testLspServer.OpenDocumentAsync(documentUri, "Text"); + + var (_, lspDocument) = await GetLspWorkspaceAndDocumentAsync(documentUri, testLspServer).ConfigureAwait(false); + AssertEx.NotNull(lspDocument); + Assert.Equal("Text", (await lspDocument.GetTextAsync(CancellationToken.None)).ToString()); + + Assert.Same(testLspServer.TestWorkspace.CurrentSolution, lspDocument.Project.Solution); + } + private static async Task OpenDocumentAndVerifyLspTextAsync(Uri documentUri, TestLspServer testLspServer, string openText = "LSP text") { await testLspServer.OpenDocumentAsync(documentUri, openText); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceRegistrationServiceTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceRegistrationServiceTests.cs index 01ab6f26a47db..538a79c16cfa4 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceRegistrationServiceTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceRegistrationServiceTests.cs @@ -10,16 +10,21 @@ using Microsoft.VisualStudio.Composition; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Workspaces; public class LspWorkspaceRegistrationServiceTests : AbstractLanguageServerProtocolTests { + public LspWorkspaceRegistrationServiceTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestDisposedWorkspaceDeregistered() { var markup = ""; TestWorkspaceRegistrationService registrationService; - using (var testLspServer = await CreateTestLspServerAsync(markup)) + await using (var testLspServer = await CreateTestLspServerAsync(markup)) { registrationService = (TestWorkspaceRegistrationService)testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); } diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index 17451f67d4886..59e27ecf372a9 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 FileTextLoader(mappedPath, parsedCommandLine.Encoding)); + loader: new WorkspaceFileTextLoader(languageServices.SolutionServices, mappedPath, parsedCommandLine.Encoding)); } } diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 1dd991b64d0a7..7284cfd1bb34c 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -15,7 +15,9 @@ using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.ResultSetTracking; using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; +using LspProtocol = Microsoft.VisualStudio.LanguageServer.Protocol; using Methods = Microsoft.VisualStudio.LanguageServer.Protocol.Methods; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator @@ -32,6 +34,21 @@ internal sealed class Generator private const bool FoldingRangeProvider = true; private const bool DiagnosticProvider = false; + private static readonly LspProtocol.ClientCapabilities LspClientCapabilities = new() + { + TextDocument = new LspProtocol.TextDocumentClientCapabilities() + { + Hover = new LspProtocol.HoverSetting() + { + ContentFormat = new[] + { + LspProtocol.MarkupKind.PlainText, + LspProtocol.MarkupKind.Markdown, + } + } + } + }; + private readonly ILsifJsonWriter _lsifJsonWriter; private readonly IdFactory _idFactory = new IdFactory(); @@ -226,7 +243,7 @@ SymbolKind.RangeVariable or // SymbolFinder.FindSymbolAtPositionAsync where if a token is both a reference and definition we'll prefer the // definition. Once we start supporting hover we'll have to remove the "original definition" part of this, since // since we show different contents for different constructed types there. - var symbolForLinkedResultSet = (declaredSymbol ?? referencedSymbol)!.OriginalDefinition; + var symbolForLinkedResultSet = (declaredSymbol ?? referencedSymbol)!.GetOriginalUnreducedDefinition(); var symbolForLinkedResultSetId = symbolResultsTracker.GetResultSetIdForSymbol(symbolForLinkedResultSet); lsifJsonWriter.Write(Edge.Create("next", lazyRangeVertex.Value.GetId(), symbolForLinkedResultSetId, idFactory)); @@ -242,7 +259,7 @@ SymbolKind.RangeVariable or // symbol but the range can point a different symbol's resultSet. This can happen if the token is // both a definition of a symbol (where we will point to the definition) but also a reference to some // other symbol. - var referenceResultsId = symbolResultsTracker.GetResultIdForSymbol(referencedSymbol.OriginalDefinition, Methods.TextDocumentReferencesName, () => new ReferenceResult(idFactory)); + var referenceResultsId = symbolResultsTracker.GetResultIdForSymbol(referencedSymbol.GetOriginalUnreducedDefinition(), Methods.TextDocumentReferencesName, () => new ReferenceResult(idFactory)); lsifJsonWriter.Write(new Item(referenceResultsId.As(), lazyRangeVertex.Value.GetId(), documentVertex.GetId(), idFactory, property: "references")); } @@ -251,7 +268,7 @@ SymbolKind.RangeVariable or // See https://github.com/Microsoft/language-server-protocol/blob/main/indexFormat/specification.md#resultset for an example. if (symbolResultsTracker.ResultSetNeedsInformationalEdgeAdded(symbolForLinkedResultSet, Methods.TextDocumentHoverName)) { - var hover = await HoverHandler.GetHoverAsync(semanticModel, syntaxToken.SpanStart, options.SymbolDescriptionOptions, languageServices, CancellationToken.None); + var hover = await HoverHandler.GetHoverAsync(semanticModel, syntaxToken.SpanStart, options.SymbolDescriptionOptions, languageServices, LspClientCapabilities, CancellationToken.None); if (hover != null) { var hoverResult = new HoverResult(hover, idFactory); diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj index ee18466f64c64..1e089abf5df1c 100644 --- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj +++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj @@ -6,15 +6,17 @@ Exe Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator true - net472 + net6.0;net472 AnyCPU $(RoslynPortableRuntimeIdentifiers) true true + false tools $(TargetsForTfmSpecificContentInPackage);_GetFilesToPackage + true $(NoWarn);NU5128 @@ -30,14 +32,14 @@ false - + - + + + - - @@ -63,6 +65,9 @@ + + + diff --git a/src/Features/Lsif/Generator/ResultSetTracking/SymbolHoldingResultSetTracker.cs b/src/Features/Lsif/Generator/ResultSetTracking/SymbolHoldingResultSetTracker.cs index 76c2633bb7e56..9a61bb009f81a 100644 --- a/src/Features/Lsif/Generator/ResultSetTracking/SymbolHoldingResultSetTracker.cs +++ b/src/Features/Lsif/Generator/ResultSetTracking/SymbolHoldingResultSetTracker.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable enable annotations - using System; using System.Collections.Generic; using System.Threading; @@ -37,7 +35,7 @@ public SymbolHoldingResultSetTracker(ILsifJsonWriter lsifJsonWriter, Compilation private TrackedResultSet GetTrackedResultSet(ISymbol symbol) { - TrackedResultSet trackedResultSet; + TrackedResultSet? trackedResultSet; // First acquire a simple read lock to see if we already have a result set; we do this with // just a read lock to ensure we aren't contending a lot if the symbol already exists which diff --git a/src/Features/Lsif/GeneratorTest/HoverTests.vb b/src/Features/Lsif/GeneratorTest/HoverTests.vb index 598b303738b9c..321282b17d007 100644 --- a/src/Features/Lsif/GeneratorTest/HoverTests.vb +++ b/src/Features/Lsif/GeneratorTest/HoverTests.vb @@ -42,28 +42,46 @@ class C Dim expectedHoverContents As String Select Case code Case "class [|C|] { string s; }" - expectedHoverContents = "class C" + expectedHoverContents = "```csharp +class C +``` + " Case "class C { void [|M|]() { } }" - expectedHoverContents = "void C.M()" + expectedHoverContents = "```csharp +void C.M() +``` + " Case "class C { string [|s|]; }" - expectedHoverContents = $"({FeaturesResources.field}) string C.s" + expectedHoverContents = $"```csharp +({FeaturesResources.field}) string C.s +``` + " Case "class C { void M(string [|s|]) { M(s); } }" - expectedHoverContents = $"({FeaturesResources.parameter}) string s" + expectedHoverContents = $"```csharp +({FeaturesResources.parameter}) string s +``` + " Case "class C { void M(string s) { string [|local|] = """"; } }" - expectedHoverContents = $"({FeaturesResources.local_variable}) string local" + expectedHoverContents = $"```csharp +({FeaturesResources.local_variable}) string local +``` + " Case " class C { /// Doc Comment void [|M|]() { } }" - expectedHoverContents = "void C.M() -Doc Comment" + expectedHoverContents = "```csharp +void C.M() +``` + +Doc Comment " Case Else Throw TestExceptionUtilities.UnexpectedValue(code) End Select - Assert.Equal(MarkupKind.PlainText, hoverMarkupContent.Kind) + Assert.Equal(MarkupKind.Markdown, hoverMarkupContent.Kind) Assert.Equal(expectedHoverContents + Environment.NewLine, hoverMarkupContent.Value) End Function @@ -99,29 +117,50 @@ class C Dim expectedHoverContents As String Select Case code Case "class C { [|string|] s; }" - expectedHoverContents = "class System.String" + expectedHoverContents = "```csharp +class System.String +``` + " Case "class C { void M() { [|M|](); } }" - expectedHoverContents = "void C.M()" + expectedHoverContents = "```csharp +void C.M() +``` + " Case "class C { void M(string s) { M([|s|]); } }" - expectedHoverContents = $"({FeaturesResources.parameter}) string s" + expectedHoverContents = $"```csharp +({FeaturesResources.parameter}) string s +``` + " Case "class C { void M(string s) { string local = """"; M([|local|]); } }" - expectedHoverContents = $"({FeaturesResources.local_variable}) string local" + expectedHoverContents = $"```csharp +({FeaturesResources.local_variable}) string local +``` + " Case "using [|S|] = System.String;" - expectedHoverContents = "class System.String" + expectedHoverContents = "```csharp +class System.String +``` + " Case "class C { [|global|]::System.String s; }" - expectedHoverContents = "" + expectedHoverContents = "```csharp + +``` + " Case " class C { /// void M() { } }" - expectedHoverContents = "void C.M()" + expectedHoverContents = "```csharp +void C.M() +``` + " Case Else Throw TestExceptionUtilities.UnexpectedValue(code) End Select - Assert.Equal(MarkupKind.PlainText, hoverMarkupContent.Kind) + Assert.Equal(MarkupKind.Markdown, hoverMarkupContent.Kind) Assert.Equal(expectedHoverContents + Environment.NewLine, hoverMarkupContent.Value) End Function @@ -161,8 +200,11 @@ class C Next Dim hoverMarkupContent = DirectCast(hoverVertex.Result.Contents.Value.Fourth, MarkupContent) - Assert.Equal(MarkupKind.PlainText, hoverMarkupContent.Kind) - Assert.Equal("class System.String" + Environment.NewLine, hoverMarkupContent.Value) + Assert.Equal(MarkupKind.Markdown, hoverMarkupContent.Kind) + Assert.Equal("```csharp +class System.String +``` + " + Environment.NewLine, hoverMarkupContent.Value) End Function End Class End Namespace diff --git a/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb b/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb index 34248e136b23c..2d78881c7e419 100644 --- a/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb +++ b/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb @@ -14,6 +14,8 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests + + diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb index 44e159e5ffc29..ae45ea26debda 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CompletionListTagCompletionProvider.vb @@ -9,6 +9,8 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers @@ -24,45 +26,46 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers MyBase.New() End Sub - Protected Overrides Async Function GetSymbolsAsync( + Protected Overrides Function GetSymbolsAsync( completionContext As CompletionContext, syntaxContext As VisualBasicSyntaxContext, position As Integer, options As CompletionOptions, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of SymbolAndSelectionInfo)) - Dim symbols = Await GetPreselectedSymbolsAsync(syntaxContext, position, options, cancellationToken).ConfigureAwait(False) - Return symbols.SelectAsArray(Function(s) New SymbolAndSelectionInfo(s, Preselect:=True)) - End Function - - Private Shared Function GetPreselectedSymbolsAsync(context As VisualBasicSyntaxContext, position As Integer, options As CompletionOptions, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of ISymbol)) - If context.SyntaxTree.IsObjectCreationTypeContext(position, cancellationToken) OrElse - context.SyntaxTree.IsInNonUserCode(position, cancellationToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() + If syntaxContext.SyntaxTree.IsObjectCreationTypeContext(position, cancellationToken) OrElse + syntaxContext.SyntaxTree.IsInNonUserCode(position, cancellationToken) Then + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() End If - If context.TargetToken.IsKind(SyntaxKind.DotToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() + If syntaxContext.TargetToken.IsKind(SyntaxKind.DotToken) Then + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() End If - Dim typeInferenceService = context.GetLanguageService(Of ITypeInferenceService)() - Dim inferredType = typeInferenceService.InferType(context.SemanticModel, position, objectAsDefault:=True, cancellationToken:=cancellationToken) + Dim typeInferenceService = syntaxContext.GetLanguageService(Of ITypeInferenceService)() + Dim inferredType = typeInferenceService.InferType(syntaxContext.SemanticModel, position, objectAsDefault:=True, cancellationToken:=cancellationToken) If inferredType Is Nothing Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() End If - Dim within = context.SemanticModel.GetEnclosingNamedType(position, cancellationToken) - Dim completionListType = GetCompletionListType(inferredType, within, context.SemanticModel.Compilation, cancellationToken) + Dim within = syntaxContext.SemanticModel.GetEnclosingNamedType(position, cancellationToken) + Dim completionListType = GetCompletionListType(inferredType, within, syntaxContext.SemanticModel.Compilation, cancellationToken) If completionListType Is Nothing Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() End If - Return Task.FromResult(completionListType.GetAccessibleMembersInThisAndBaseTypes(Of ISymbol)(within) _ - .WhereAsArray(Function(m) m.MatchesKind(SymbolKind.Field, SymbolKind.Property) AndAlso - m.IsStatic AndAlso - m.IsAccessibleWithin(within) AndAlso - m.IsEditorBrowsable(options.HideAdvancedMembers, context.SemanticModel.Compilation))) + Dim builder = ArrayBuilder(Of SymbolAndSelectionInfo).GetInstance() + For Each member In completionListType.GetAccessibleMembersInThisAndBaseTypes(Of ISymbol)(within) + If member.MatchesKind(SymbolKind.Field, SymbolKind.Property) AndAlso + member.IsStatic AndAlso + member.IsAccessibleWithin(within) AndAlso + member.IsEditorBrowsable(options.HideAdvancedMembers, syntaxContext.SemanticModel.Compilation) Then + builder.Add(New SymbolAndSelectionInfo(member, Preselect:=True)) + End If + Next + + Return Task.FromResult(builder.ToImmutableAndFree()) End Function Private Shared Function GetCompletionListType(inferredType As ITypeSymbol, within As INamedTypeSymbol, compilation As Compilation, cancellationToken As CancellationToken) As ITypeSymbol @@ -95,16 +98,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers context As VisualBasicSyntaxContext, supportedPlatformData As SupportedPlatformData) As CompletionItem + ' Use symbol name (w/o containing type) as additional filter text, which would + ' promote this item during matching when user types member name only, Like "Empty" + ' instead of "ImmutableArray.Empty" + Dim additionalFilterTexts = ImmutableArray.Create(symbols(0).Symbol.Name) Return SymbolCompletionItem.CreateWithSymbolId( displayText:=displayText, displayTextSuffix:=displayTextSuffix, insertionText:=insertionText, - filterText:=GetFilterTextDefault(symbols(0).Symbol, displayText, context), + filterText:=displayText, symbols:=symbols.SelectAsArray(Function(t) t.Symbol), rules:=CompletionItemRules.Default.WithMatchPriority(MatchPriority.Preselect), contextPosition:=context.Position, sortText:=displayText, - supportedPlatforms:=supportedPlatformData) + supportedPlatforms:=supportedPlatformData, + tags:=WellKnownTagArrays.TargetTypeMatch).WithAdditionalFilterTexts(additionalFilterTexts) End Function End Class End Namespace diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb index 17a962fdd9e2c..ba7d41ad967fc 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb @@ -9,7 +9,8 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService -Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery @@ -25,83 +26,55 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Public Sub New() End Sub + Private Shared ReadOnly s_enumMemberCompletionItemRules As CompletionItemRules = CompletionItemRules.Default.WithMatchPriority(MatchPriority.Preselect) + Friend Overrides ReadOnly Property Language As String Get Return LanguageNames.VisualBasic End Get End Property - Private Shared Function GetPreselectedSymbolsAsync( - context As VisualBasicSyntaxContext, position As Integer, options As CompletionOptions, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of ISymbol)) - - If context.SyntaxTree.IsInNonUserCode(context.Position, cancellationToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If - - ' This providers provides fully qualified names, eg "DayOfWeek.Monday" - ' Don't run after dot because SymbolCompletionProvider will provide - ' members in situations like Dim x = DayOfWeek.$$ - If context.TargetToken.IsKind(SyntaxKind.DotToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If - - Dim typeInferenceService = context.GetLanguageService(Of ITypeInferenceService)() - Dim enumType = typeInferenceService.InferType(context.SemanticModel, position, objectAsDefault:=True, cancellationToken:=cancellationToken) - - If enumType.TypeKind <> TypeKind.Enum Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If - - ' We'll want to build a list of the actual enum members and all accessible instances of that enum, too - Dim result = enumType.GetMembers().Where( - Function(m As ISymbol) As Boolean - Return m.Kind = SymbolKind.Field AndAlso - DirectCast(m, IFieldSymbol).IsConst AndAlso - m.IsEditorBrowsable(options.HideAdvancedMembers, context.SemanticModel.Compilation) - End Function).ToImmutableArray() - - Return Task.FromResult(result) - End Function - - Private Shared Function GetNormalSymbolsAsync( - context As VisualBasicSyntaxContext, position As Integer, options As CompletionOptions, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of ISymbol)) + Protected Overrides Function GetSymbolsAsync( + completionContext As CompletionContext, + syntaxContext As VisualBasicSyntaxContext, + position As Integer, + options As CompletionOptions, + cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of SymbolAndSelectionInfo)) - If context.SyntaxTree.IsInNonUserCode(context.Position, cancellationToken) OrElse - context.SyntaxTree.IsInSkippedText(position, cancellationToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If + Dim builder = ArrayBuilder(Of SymbolAndSelectionInfo).GetInstance() + Try - If context.TargetToken.IsKind(SyntaxKind.DotToken) Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If + If syntaxContext.SyntaxTree.IsInNonUserCode(syntaxContext.Position, cancellationToken) Then + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() + End If - Dim typeInferenceService = context.GetLanguageService(Of ITypeInferenceService)() - Dim span = New TextSpan(position, 0) - Dim enumType = typeInferenceService.InferType(context.SemanticModel, position, objectAsDefault:=True, cancellationToken:=cancellationToken) + ' This providers provides fully qualified names, eg "DayOfWeek.Monday" + ' Don't run after dot because SymbolCompletionProvider will provide + ' members in situations like Dim x = DayOfWeek.$$ + If syntaxContext.TargetToken.IsKind(SyntaxKind.DotToken) Then + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() + End If - If enumType.TypeKind <> TypeKind.Enum Then - Return SpecializedTasks.EmptyImmutableArray(Of ISymbol)() - End If + Dim typeInferenceService = syntaxContext.GetLanguageService(Of ITypeInferenceService)() + Dim enumType = typeInferenceService.InferType(syntaxContext.SemanticModel, position, objectAsDefault:=True, cancellationToken:=cancellationToken) - Dim otherSymbols = context.SemanticModel.LookupSymbols(position).WhereAsArray( - Function(s) s.MatchesKind(SymbolKind.Field, SymbolKind.Local, SymbolKind.Parameter, SymbolKind.Property) AndAlso - s.IsEditorBrowsable(options.HideAdvancedMembers, context.SemanticModel.Compilation)) + If enumType.TypeKind <> TypeKind.Enum Then + Return SpecializedTasks.EmptyImmutableArray(Of SymbolAndSelectionInfo)() + End If - Dim otherInstances = otherSymbols.WhereAsArray(Function(s) Equals(enumType, GetTypeFromSymbol(s))) + builder.Add(New SymbolAndSelectionInfo(enumType, Preselect:=False)) - Return Task.FromResult(otherInstances.Concat(enumType)) - End Function + For Each member In enumType.GetMembers() + If member.Kind = SymbolKind.Field AndAlso DirectCast(member, IFieldSymbol).IsConst AndAlso member.IsEditorBrowsable(options.HideAdvancedMembers, syntaxContext.SemanticModel.Compilation) Then + builder.Add(New SymbolAndSelectionInfo(member, Preselect:=True)) + End If + Next - Protected Overrides Async Function GetSymbolsAsync( - completionContext As CompletionContext, - syntaxContext As VisualBasicSyntaxContext, - position As Integer, - options As CompletionOptions, - cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of SymbolAndSelectionInfo)) - Dim normalSymbols = Await GetNormalSymbolsAsync(syntaxContext, position, options, cancellationToken).ConfigureAwait(False) - Dim preselectSymbols = Await GetPreselectedSymbolsAsync(syntaxContext, position, options, cancellationToken).ConfigureAwait(False) + Return Task.FromResult(builder.ToImmutable()) - Return normalSymbols.SelectAsArray(Function(s) New SymbolAndSelectionInfo(s, Preselect:=False)).Concat(preselectSymbols.SelectAsArray(Function(s) New SymbolAndSelectionInfo(s, Preselect:=True))) + Finally + builder.Free() + End Try End Function Public Overrides Function IsInsertionTrigger(text As SourceText, characterPosition As Integer, options As CompletionOptions) As Boolean @@ -123,12 +96,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function ' PERF: Cached values for GetDisplayAndInsertionText. Cuts down on the number of calls to ToMinimalDisplayString for large enums. + Private ReadOnly _gate As New Object() Private _cachedDisplayAndInsertionTextContainingType As INamedTypeSymbol Private _cachedDisplayAndInsertionTextContext As VisualBasicSyntaxContext Private _cachedDisplayAndInsertionTextContainingTypeText As String Protected Overrides Function GetDisplayAndSuffixAndInsertionText(symbol As ISymbol, context As VisualBasicSyntaxContext) As (displayText As String, suffix As String, insertionText As String) - If symbol.ContainingType IsNot Nothing AndAlso symbol.ContainingType.TypeKind = TypeKind.Enum Then + If symbol.Kind <> SymbolKind.Field Then + Return CompletionUtilities.GetDisplayAndSuffixAndInsertionText(symbol, context) + End If + + ' Completion service allows concurrent calls + SyncLock _gate If Not Equals(_cachedDisplayAndInsertionTextContainingType, symbol.ContainingType) OrElse _cachedDisplayAndInsertionTextContext IsNot context Then Dim displayFormat = SymbolDisplayFormat.MinimallyQualifiedFormat.WithMemberOptions(SymbolDisplayMemberOptions.IncludeContainingType).WithLocalOptions(SymbolDisplayLocalOptions.None) Dim displayService = context.GetLanguageService(Of ISymbolDisplayService)() @@ -137,11 +116,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers _cachedDisplayAndInsertionTextContext = context End If - Dim text As String = _cachedDisplayAndInsertionTextContainingTypeText & "." & symbol.Name + Dim text = _cachedDisplayAndInsertionTextContainingTypeText & "." & symbol.Name Return (text, "", text) - End If - - Return CompletionUtilities.GetDisplayAndSuffixAndInsertionText(symbol, context) + End SyncLock End Function Protected Overrides Function CreateItem( @@ -154,18 +131,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers supportedPlatformData As SupportedPlatformData) As CompletionItem Dim preselect = symbols.Any(Function(t) t.Preselect) - Dim rules = CompletionItemRules.Default.WithMatchPriority(If(preselect, MatchPriority.Preselect, MatchPriority.Default)) + Dim rules = If(preselect, s_enumMemberCompletionItemRules, CompletionItemRules.Default) - Return SymbolCompletionItem.CreateWithSymbolId( + Dim item = SymbolCompletionItem.CreateWithSymbolId( displayText:=displayText, displayTextSuffix:=displayTextSuffix, insertionText:=insertionText, - filterText:=GetFilterTextDefault(symbols(0).Symbol, displayText, context), + filterText:=displayText, symbols:=symbols.SelectAsArray(Function(t) t.Symbol), contextPosition:=context.Position, sortText:=insertionText, supportedPlatforms:=supportedPlatformData, rules:=rules) + + ' Use member name (w/o enum type name) as additional filter text, which would + ' promote this item during matching when user types member name only, Like "Red" + ' instead of "Colors.Empty" + If symbols(0).Symbol.Kind = SymbolKind.Field Then + item = item.AddTag(WellKnownTags.TargetTypeMatch).WithAdditionalFilterTexts(ImmutableArray.Create(symbols(0).Symbol.Name)) + End If + + Return item End Function Protected Overrides Function GetInsertionText(item As CompletionItem, ch As Char) As String diff --git a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb index 8886d7639d290..ea8dbe26a9745 100644 --- a/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb +++ b/src/Features/VisualBasic/Portable/Completion/VisualBasicCompletionService.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.CodeAnalysis.Tags Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers @@ -20,13 +21,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion Friend Class Factory Implements ILanguageServiceFactory + Private ReadOnly _listenerProvider As IAsynchronousOperationListenerProvider + - Public Sub New() + Public Sub New(listenerProvider As IAsynchronousOperationListenerProvider) + _listenerProvider = listenerProvider End Sub Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService - Return New VisualBasicCompletionService(languageServices.LanguageServices.SolutionServices) + Return New VisualBasicCompletionService(languageServices.LanguageServices.SolutionServices, _listenerProvider) End Function End Class @@ -36,8 +40,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion defaultCommitCharacters:=CompletionRules.Default.DefaultCommitCharacters, defaultEnterKeyRule:=EnterKeyRule.Always) - Private Sub New(services As SolutionServices) - MyBase.New(services) + Private Sub New(services As SolutionServices, listenerProvider As IAsynchronousOperationListenerProvider) + MyBase.New(services, listenerProvider) End Sub Public Overrides ReadOnly Property Language As String diff --git a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb index 58d3e78f30f68..df5453f38b45e 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb @@ -25,6 +25,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList Protected Overrides ReadOnly Property ShouldMoveCloseBraceToNewLine As Boolean = False + Protected Overrides Function FirstToken(listSyntax As ArgumentListSyntax) As SyntaxToken + Return listSyntax.OpenParenToken + End Function + + Protected Overrides Function LastToken(listSyntax As ArgumentListSyntax) As SyntaxToken + Return listSyntax.CloseParenToken + End Function + Protected Overrides Function GetListItems(listSyntax As ArgumentListSyntax) As SeparatedSyntaxList(Of ArgumentSyntax) Return listSyntax.Arguments End Function diff --git a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicCollectionCreationExpressionWrapper.vb b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicCollectionCreationExpressionWrapper.vb index a0238f700ea9f..13a5354893e26 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicCollectionCreationExpressionWrapper.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicCollectionCreationExpressionWrapper.vb @@ -40,6 +40,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList Protected Overrides ReadOnly Property ShouldMoveCloseBraceToNewLine As Boolean = True + Protected Overrides Function FirstToken(listSyntax As CollectionInitializerSyntax) As SyntaxToken + Return listSyntax.OpenBraceToken + End Function + + Protected Overrides Function LastToken(listSyntax As CollectionInitializerSyntax) As SyntaxToken + Return listSyntax.CloseBraceToken + End Function + Protected Overrides Function GetListItems(listSyntax As CollectionInitializerSyntax) As SeparatedSyntaxList(Of ExpressionSyntax) Return listSyntax.Initializers End Function diff --git a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicParameterWrapper.vb b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicParameterWrapper.vb index cc6fcc4fb81b8..fa5f86ba4fe18 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicParameterWrapper.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicParameterWrapper.vb @@ -26,6 +26,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList Protected Overrides ReadOnly Property ShouldMoveCloseBraceToNewLine As Boolean = False + Protected Overrides Function FirstToken(listSyntax As ParameterListSyntax) As SyntaxToken + Return listSyntax.OpenParenToken + End Function + + Protected Overrides Function LastToken(listSyntax As ParameterListSyntax) As SyntaxToken + Return listSyntax.CloseParenToken + End Function + Protected Overrides Function GetListItems(listSyntax As ParameterListSyntax) As SeparatedSyntaxList(Of ParameterSyntax) Return listSyntax.Parameters End Function diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs index 6e3d51c9bf249..b1bde4f836a6b 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs @@ -130,7 +130,7 @@ private async Task TryStartAndInitializeProcessAsync(C catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) #pragma warning restore CA2016 { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.RemoteService.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.RemoteService.cs index 952ea75042664..b0804d640276b 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.RemoteService.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.RemoteService.cs @@ -102,7 +102,7 @@ private async Task ProcessExitedHandlerAsync() } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.cs index 057de7bb967f2..58d019c05b0a3 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.cs @@ -262,7 +262,7 @@ private async Task TryGetOrCreateRemoteServiceAsync() } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } return default; @@ -349,7 +349,7 @@ public async Task ResetAsync(InteractiveHostOptions optio } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Interactive/HostProcess/InteractiveHost32.csproj b/src/Interactive/HostProcess/InteractiveHost32.csproj index 7b0f0fa7f69fa..b43f4aaa790c8 100644 --- a/src/Interactive/HostProcess/InteractiveHost32.csproj +++ b/src/Interactive/HostProcess/InteractiveHost32.csproj @@ -8,6 +8,7 @@ net472 true true + true diff --git a/src/Interactive/HostProcess/InteractiveHost64.csproj b/src/Interactive/HostProcess/InteractiveHost64.csproj index e4fb2617f80f6..3ab7544393a23 100644 --- a/src/Interactive/HostProcess/InteractiveHost64.csproj +++ b/src/Interactive/HostProcess/InteractiveHost64.csproj @@ -9,6 +9,7 @@ win10-x64 true true + true true diff --git a/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj b/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj index 62b48e61e2beb..2f89f3b4ce210 100644 --- a/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj +++ b/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj @@ -4,7 +4,7 @@ Library Roslyn.InteractiveHost.UnitTests - net6.0 + net7.0 diff --git a/src/Interactive/csi/csi.csproj b/src/Interactive/csi/csi.csproj index 0c62fccaecf34..e38d9b61d4408 100644 --- a/src/Interactive/csi/csi.csproj +++ b/src/Interactive/csi/csi.csproj @@ -7,6 +7,7 @@ CSharpInteractive net6.0;net472 false + true diff --git a/src/Interactive/vbi/vbi.vbproj b/src/Interactive/vbi/vbi.vbproj index 46de599bd887b..e3ad0e2fe6026 100644 --- a/src/Interactive/vbi/vbi.vbproj +++ b/src/Interactive/vbi/vbi.vbproj @@ -8,6 +8,7 @@ net6.0;net472 false + false 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/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj b/src/Scripting/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj index 0828933d1d2ac..5c984fea1da5e 100644 --- a/src/Scripting/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj +++ b/src/Scripting/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests true - net6.0;net472 + net7.0;net472 diff --git a/src/Scripting/CoreTest/Microsoft.CodeAnalysis.Scripting.UnitTests.csproj b/src/Scripting/CoreTest/Microsoft.CodeAnalysis.Scripting.UnitTests.csproj index 51876d7af617f..c97eb8634c4f5 100644 --- a/src/Scripting/CoreTest/Microsoft.CodeAnalysis.Scripting.UnitTests.csproj +++ b/src/Scripting/CoreTest/Microsoft.CodeAnalysis.Scripting.UnitTests.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.Scripting.Test true - net6.0;net472 + net7.0;net472 diff --git a/src/Scripting/CoreTestUtilities/Microsoft.CodeAnalysis.Scripting.TestUtilities.csproj b/src/Scripting/CoreTestUtilities/Microsoft.CodeAnalysis.Scripting.TestUtilities.csproj index a758477041bfc..26f1b6c053366 100644 --- a/src/Scripting/CoreTestUtilities/Microsoft.CodeAnalysis.Scripting.TestUtilities.csproj +++ b/src/Scripting/CoreTestUtilities/Microsoft.CodeAnalysis.Scripting.TestUtilities.csproj @@ -4,7 +4,7 @@ Library true - net472;net6.0 + net472;net7.0 true false true 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, IEnumerablenetcoreapp3.1;netstandard2.0 None + false diff --git a/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj b/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj index ea94e133c607e..fe28f76ce9225 100644 --- a/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj +++ b/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj @@ -54,6 +54,7 @@ + diff --git a/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs b/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs index 49d589f69ae21..a6f187ce0e361 100644 --- a/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs +++ b/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs @@ -51,7 +51,7 @@ public int GetSymAttribute(int methodToken, string name, int bufferLength, out i // The EE should never be calling ISymUnmanagedReader.GetSymAttribute. // In order to account for EnC updates, it should always be calling // ISymUnmanagedReader3.GetSymAttributeByVersion instead. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public int GetSymAttributeByVersion(int methodToken, int version, string name, int bufferLength, out int count, byte[] customDebugInformation) diff --git a/src/Test/PdbUtilities/Roslyn.Test.PdbUtilities.csproj b/src/Test/PdbUtilities/Roslyn.Test.PdbUtilities.csproj index 2533d26f9d625..73c7068bdf9b8 100644 --- a/src/Test/PdbUtilities/Roslyn.Test.PdbUtilities.csproj +++ b/src/Test/PdbUtilities/Roslyn.Test.PdbUtilities.csproj @@ -4,7 +4,7 @@ Library Roslyn.Test.PdbUtilities - net6.0;net472 + net6.0;net7.0;net472 true false diff --git a/src/Test/Perf/StackDepthTest/Program.cs b/src/Test/Perf/StackDepthTest/Program.cs index fba85a290f88c..0ec6f3905b7bd 100644 --- a/src/Test/Perf/StackDepthTest/Program.cs +++ b/src/Test/Perf/StackDepthTest/Program.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; using System; using System.Runtime.InteropServices; using System.Text; @@ -75,7 +76,7 @@ private static void CompileCode(string stringText) { var parseOptions = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None); var options = new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary, concurrentBuild: false); - var tree = SyntaxFactory.ParseSyntaxTree(stringText, parseOptions); + var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(stringText, encoding: null, SourceHashAlgorithm.Sha256), parseOptions); var reference = MetadataReference.CreateFromFile(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\mscorlib.dll"); var comp = CSharpCompilation.Create("assemblyName", new SyntaxTree[] { tree }, references: new MetadataReference[] { reference }, options: options); var diag = comp.GetDiagnostics(); diff --git a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj index d99d5b18fffa4..bf101c9ec5e4c 100644 --- a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj +++ b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj @@ -4,7 +4,7 @@ Exe - net6.0;net472 + net7.0;net472 false AnyCPU true @@ -12,7 +12,7 @@ true - + true $(NoWarn),CA2007 diff --git a/src/Tools/IdeBenchmarks/Lsp/LspCompletionBenchmarks.cs b/src/Tools/IdeBenchmarks/Lsp/LspCompletionBenchmarks.cs index d39d2d14b1d0c..eddacacbf990b 100644 --- a/src/Tools/IdeBenchmarks/Lsp/LspCompletionBenchmarks.cs +++ b/src/Tools/IdeBenchmarks/Lsp/LspCompletionBenchmarks.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.UnitTests.Completion; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; @@ -24,6 +23,10 @@ public class LspCompletionBenchmarks : AbstractLanguageServerProtocolTests private IGlobalOptionService? _globalOptionService; private LSP.CompletionParams? _completionParams; + public LspCompletionBenchmarks() : base(null) + { + } + [GlobalSetup] public void GlobalSetup() { @@ -103,9 +106,12 @@ public void GetCompletionsWithTextEdits() } [IterationCleanup] - public void Cleanup() + public async Task CleanupAsync() { - _testServer?.Dispose(); + if (_testServer is not null) + { + await _testServer.DisposeAsync(); + } _useExportProviderAttribute.After(null); } } diff --git a/src/Tools/IdeCoreBenchmarks/IdeCoreBenchmarks.csproj b/src/Tools/IdeCoreBenchmarks/IdeCoreBenchmarks.csproj index 0b3f623eabc80..24c145bef3dc4 100644 --- a/src/Tools/IdeCoreBenchmarks/IdeCoreBenchmarks.csproj +++ b/src/Tools/IdeCoreBenchmarks/IdeCoreBenchmarks.csproj @@ -5,7 +5,7 @@ Exe - net6.0;net472 + net7.0;net472 false AnyCPU diff --git a/src/Tools/IdeCoreBenchmarks/NavigateToBenchmarks.cs b/src/Tools/IdeCoreBenchmarks/NavigateToBenchmarks.cs index 6f46760cf7215..4cc71a9c1729c 100644 --- a/src/Tools/IdeCoreBenchmarks/NavigateToBenchmarks.cs +++ b/src/Tools/IdeCoreBenchmarks/NavigateToBenchmarks.cs @@ -197,19 +197,15 @@ public async Task RunNavigateTo() Console.WriteLine("Time to search: " + (DateTime.Now - start)); } - private async Task SearchAsync(Project project, ImmutableArray priorityDocuments) + private static async Task SearchAsync(Project project, ImmutableArray priorityDocuments) { var service = project.Services.GetService(); var results = new List(); - await service.SearchProjectAsync( - project, priorityDocuments, "Syntax", service.KindsProvided, activeDocument: null, - r => - { - lock (results) - results.Add(r); - - return Task.CompletedTask; - }, CancellationToken.None); + await foreach (var item in service.SearchProjectAsync( + project, priorityDocuments, "Syntax", service.KindsProvided, activeDocument: null, CancellationToken.None)) + { + results.Add(item); + } return results.Count; } diff --git a/src/Tools/IdeCoreBenchmarks/SerializationBenchmarks.cs b/src/Tools/IdeCoreBenchmarks/SerializationBenchmarks.cs index 0a5187507f4b3..5041363a254d4 100644 --- a/src/Tools/IdeCoreBenchmarks/SerializationBenchmarks.cs +++ b/src/Tools/IdeCoreBenchmarks/SerializationBenchmarks.cs @@ -11,6 +11,7 @@ using BenchmarkDotNet.Attributes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace IdeCoreBenchmarks { @@ -39,7 +40,7 @@ public void GlobalSetup() } var text = File.ReadAllText(file); - var tree = SyntaxFactory.ParseSyntaxTree(text); + var tree = SyntaxFactory.ParseSyntaxTree(SourceText.From(text, encoding: null, SourceHashAlgorithms.Default)); _rootList.Add(tree.GetCompilationUnitRoot()); } } diff --git a/src/Tools/PrepareTests/PrepareTests.csproj b/src/Tools/PrepareTests/PrepareTests.csproj index b0c04b5e2db3b..2bce3d82cd086 100644 --- a/src/Tools/PrepareTests/PrepareTests.csproj +++ b/src/Tools/PrepareTests/PrepareTests.csproj @@ -3,7 +3,7 @@ Exe - net6.0 + net7.0 false false false diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj index 2eee22c003800..0ca9374ab9645 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj @@ -9,7 +9,7 @@ Roslyn.Compilers.Internal.BoundTreeGenerator BoundTreeGenerator True - net6.0 + net7.0 false diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj index 0781b1afab3a3..b610e76dad197 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj @@ -8,7 +8,7 @@ Exe Roslyn.Compilers.CSharp.Internal.CSharpErrorFactsGenerator True - net6.0 + net7.0 false \ No newline at end of file diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj index 7eae8afa2756a..3d912c842d415 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj @@ -7,9 +7,9 @@ AnyCPU CSharpSyntaxGenerator True - - net6.0;netstandard2.0 + net7.0;netstandard2.0 Exe $(RoslynPortableRuntimeIdentifiers) false diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/CompilersIOperationGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/CompilersIOperationGenerator.csproj index d77ab5532f705..4d402c3555179 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/CompilersIOperationGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/CompilersIOperationGenerator.csproj @@ -6,7 +6,7 @@ Roslyn.Compilers.Internal.IOperationGenerator IOperationGenerator True - net6.0 + net7.0 false diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/IOperationClassWriter.cs b/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/IOperationClassWriter.cs index 51f3467e0669f..f34ab0c48e4f9 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/IOperationClassWriter.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/IOperationClassWriter.cs @@ -935,7 +935,7 @@ private void WriteCloner() WriteLine("public OperationCloner() { }"); WriteLine(@"[return: NotNullIfNotNull(""node"")]"); WriteLine("private T? Visit(T? node) where T : IOperation? => (T?)Visit(node, argument: null);"); - WriteLine("public override IOperation DefaultVisit(IOperation operation, object? argument) => throw ExceptionUtilities.Unreachable;"); + WriteLine("public override IOperation DefaultVisit(IOperation operation, object? argument) => throw ExceptionUtilities.Unreachable();"); WriteLine("private ImmutableArray VisitArray(ImmutableArray nodes) where T : IOperation => nodes.SelectAsArray((n, @this) => @this.Visit(n), this)!;"); WriteLine("private ImmutableArray<(ISymbol, T)> VisitArray(ImmutableArray<(ISymbol, T)> nodes) where T : IOperation => nodes.SelectAsArray((n, @this) => (n.Item1, @this.Visit(n.Item2)), this)!;"); diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj index 2c0932365b44e..cd11ed703bec4 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj @@ -10,7 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic.Internal.VBErrorFactsGenerator VBErrorFactsGenerator True - net6.0 + net7.0 false \ No newline at end of file diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj index 9bea73d8fb3bc..dc03c3cc1cbc2 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj @@ -11,7 +11,7 @@ VBSyntaxGenerator Off True - net6.0 + net7.0 false diff --git a/src/Tools/Source/RunTests/AssemblyScheduler.cs b/src/Tools/Source/RunTests/AssemblyScheduler.cs index 5a1fa5c5206a1..1128843fe7cec 100644 --- a/src/Tools/Source/RunTests/AssemblyScheduler.cs +++ b/src/Tools/Source/RunTests/AssemblyScheduler.cs @@ -18,7 +18,19 @@ namespace RunTests { internal record struct WorkItemInfo(ImmutableSortedDictionary> Filters, int PartitionIndex) { - internal string DisplayName => $"{string.Join("_", Filters.Keys.Select(a => Path.GetFileNameWithoutExtension(a.AssemblyName)))}_{PartitionIndex}"; + internal string DisplayName + { + get + { + var assembliesString = string.Join("_", Filters.Keys.Select(a => Path.GetFileNameWithoutExtension(a.AssemblyName))); + + // Currently some helix APIs don't work when the work item friendly name is more than 200 characters. + // Until that is fixed we manually truncate the name ourselves to a reasonable limit. + // https://github.com/dotnet/arcade/issues/11079 + assembliesString = assembliesString.Length > 150 ? $"{assembliesString[..150]}..." : assembliesString; + return $"{assembliesString}_{PartitionIndex}"; + } + } } internal sealed class AssemblyScheduler diff --git a/src/Tools/Source/RunTests/RunTests.csproj b/src/Tools/Source/RunTests/RunTests.csproj index 4127eba2b292a..a7d645b1196c7 100644 --- a/src/Tools/Source/RunTests/RunTests.csproj +++ b/src/Tools/Source/RunTests/RunTests.csproj @@ -3,7 +3,7 @@ Exe - net6.0 + net7.0 true false false diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs index c4cc99d289c93..70a081abd587a 100644 --- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs +++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs @@ -37,6 +37,8 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.CodeModel { internal partial class CSharpCodeModelService : AbstractCodeModelService { + private static readonly SyntaxTree s_emptyTree = SyntaxFactory.ParseSyntaxTree(SourceText.From("", encoding: null, SourceHashAlgorithms.Default)); + internal CSharpCodeModelService( HostLanguageServices languageServiceProvider, EditorOptionsService editorOptionsService, @@ -3045,7 +3047,7 @@ protected override Accessibility GetDefaultAccessibility(SymbolKind targetSymbol var tree = compilation.SyntaxTrees.FirstOrDefault(); if (tree == null) { - tree = SyntaxFactory.ParseSyntaxTree(""); + tree = s_emptyTree; compilation = compilation.AddSyntaxTrees(tree); } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs index cfd0a508d00d8..0a4dc939ffd37 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs @@ -20,7 +20,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitTypeDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitTypeDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; @@ -29,7 +29,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTypeDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTypeDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; @@ -38,7 +38,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddBracesDiagnosticId)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_required_braces_for_single_line_control_statements))] public static readonly FixIdDefinition? AddBracesDiagnosticId; @@ -47,7 +47,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; @@ -56,7 +56,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; @@ -65,7 +65,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForConversionOperatorsDiagnosticId; @@ -74,7 +74,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForOperatorsDiagnosticId; @@ -83,7 +83,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForPropertiesDiagnosticId; @@ -92,7 +92,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; @@ -101,7 +101,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; @@ -110,7 +110,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineDeclarationDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineDeclarationDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; @@ -119,7 +119,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/dotnet/csharp/misc/cs0168")] + [HelpLink("https://learn.microsoft.com/dotnet/csharp/misc/cs0168")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] public static readonly FixIdDefinition? CS0168; @@ -128,7 +128,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/dotnet/csharp/misc/cs0168")] + [HelpLink("https://learn.microsoft.com/dotnet/csharp/misc/cs0168")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] public static readonly FixIdDefinition? CS0219; @@ -155,7 +155,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? ConvertSwitchStatementToExpressionDiagnosticId; @@ -173,7 +173,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineAsTypeCheckId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineAsTypeCheckId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineAsTypeCheckId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? InlineAsTypeCheckId; @@ -182,7 +182,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineIsTypeCheckId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineIsTypeCheckId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineIsTypeCheckId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? InlineIsTypeCheckId; @@ -191,7 +191,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_conditional_delegate_call_preferences))] public static readonly FixIdDefinition? InvokeDelegateWithConditionalAccessId; @@ -200,7 +200,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_static_local_function_preferences))] public static readonly FixIdDefinition? MakeLocalFunctionStaticDiagnosticId; @@ -209,7 +209,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MakeStructReadOnlyDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeStructReadOnlyDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeStructReadOnlyDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_readonly_struct_preferences))] public static readonly FixIdDefinition? MakeStructReadOnlyDiagnosticId; @@ -218,7 +218,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_method_group_conversion_preferences))] public static readonly FixIdDefinition? RemoveUnnecessaryLambdaExpressionDiagnosticId; @@ -227,7 +227,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? SimplifyPropertyPatternDiagnosticId; @@ -236,7 +236,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseDeconstructionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDeconstructionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDeconstructionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] public static readonly FixIdDefinition? UseDeconstructionDiagnosticId; @@ -245,7 +245,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseDefaultLiteralDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDefaultLiteralDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDefaultLiteralDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_default_T_preferences))] public static readonly FixIdDefinition? UseDefaultLiteralDiagnosticId; @@ -254,7 +254,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForLambdaExpressionsDiagnosticId; @@ -263,7 +263,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForLocalFunctionsDiagnosticId; @@ -272,7 +272,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_namespace_preferences))] public static readonly FixIdDefinition? UseFileScopedNamespaceDiagnosticId; @@ -281,7 +281,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_new_preferences))] public static readonly FixIdDefinition? UseImplicitObjectCreationDiagnosticId; @@ -290,7 +290,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIndexOperatorDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIndexOperatorDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] public static readonly FixIdDefinition? UseIndexOperatorDiagnosticId; @@ -299,7 +299,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseLocalFunctionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseLocalFunctionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseLocalFunctionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_local_over_anonymous_function_preferences))] public static readonly FixIdDefinition? UseLocalFunctionDiagnosticId; @@ -308,7 +308,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNotPatternDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNotPatternDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNotPatternDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UseNotPatternDiagnosticId; @@ -317,7 +317,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UseNullCheckOverTypeCheckDiagnosticId; @@ -326,7 +326,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UsePatternCombinatorsDiagnosticId; @@ -335,7 +335,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseRangeOperatorDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseRangeOperatorDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseRangeOperatorDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] public static readonly FixIdDefinition? UseRangeOperatorDiagnosticId; @@ -344,7 +344,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_using_statement_preferences))] public static readonly FixIdDefinition? UseSimpleUsingStatementDiagnosticId; @@ -353,7 +353,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseThrowExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseThrowExpressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseThrowExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_throw_expression_preferences))] public static readonly FixIdDefinition? UseThrowExpressionDiagnosticId; @@ -362,7 +362,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseTupleSwapDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseTupleSwapDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseTupleSwapDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] public static readonly FixIdDefinition? UseTupleSwapDiagnosticId; @@ -371,7 +371,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveRedundantNullableDirectiveDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveRedundantNullableDirectiveDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveRedundantNullableDirectiveDiagnosticId}")] [LocalizedName(typeof(CSharpAnalyzersResources), nameof(CSharpAnalyzersResources.Remove_unnecessary_nullable_directive))] public static readonly FixIdDefinition? RemoveRedundantNullableDirectiveDiagnosticId; @@ -380,7 +380,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryNullableDirectiveDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryNullableDirectiveDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryNullableDirectiveDiagnosticId}")] [LocalizedName(typeof(CSharpAnalyzersResources), nameof(CSharpAnalyzersResources.Remove_unnecessary_nullable_directive))] public static readonly FixIdDefinition? RemoveUnnecessaryNullableDirectiveDiagnosticId; } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCreateServicesOnTextViewConnection.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCreateServicesOnTextViewConnection.cs index c2440b16009ad..156dd2b9f63ad 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCreateServicesOnTextViewConnection.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCreateServicesOnTextViewConnection.cs @@ -37,15 +37,15 @@ public CSharpCreateServicesOnTextViewConnection( { } - protected override async Task InitializeServiceForOpenedDocumentAsync(Document document) + protected override async Task InitializeServiceForProjectWithOpenedDocumentAsync(Project project) { // Only pre-populate cache if import completion is enabled if (GlobalOptions.GetOption(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp) != true) return; - var service = document.GetRequiredLanguageService(); - service.QueueCacheWarmUpTask(document.Project); - await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(document.Project, CancellationToken.None).ConfigureAwait(false); + var service = project.GetRequiredLanguageService(); + service.QueueCacheWarmUpTask(project); + await ExtensionMethodImportCompletionHelper.WarmUpCacheAsync(project, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/CSharp/Impl/Microsoft.VisualStudio.LanguageServices.CSharp.csproj b/src/VisualStudio/CSharp/Impl/Microsoft.VisualStudio.LanguageServices.CSharp.csproj index d6ce22452c6bd..768223425076b 100644 --- a/src/VisualStudio/CSharp/Impl/Microsoft.VisualStudio.LanguageServices.CSharp.csproj +++ b/src/VisualStudio/CSharp/Impl/Microsoft.VisualStudio.LanguageServices.CSharp.csproj @@ -8,6 +8,7 @@ Microsoft.VisualStudio.LanguageServices.CSharp true full + true false diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs index c3032d346bcee..ae2a7a29f28e0 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop; using Roslyn.Utilities; @@ -23,7 +24,7 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim /// /// This class is free-threaded. /// - 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/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs index a10722fc7775c..f0503dafe78db 100644 --- a/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs +++ b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs @@ -2,13 +2,14 @@ // 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.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DesignerAttribute; +using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Utilities; @@ -19,37 +20,40 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.DesignerAttri [UseExportProvider] public class DesignerAttributeServiceTests { - [Fact] - public async Task NoDesignerTest1() + private static readonly TestComposition s_inProcessComposition = EditorTestCompositions.EditorFeatures; + private static readonly TestComposition s_outOffProcessComposition = s_inProcessComposition.WithTestHostParts(TestHost.OutOfProcess); + + [Theory, CombinatorialData] + public async Task NoDesignerTest1(TestHost host) { var code = @"class Test { }"; - await TestAsync(code, category: null); + await TestAsync(code, category: null, host); } - [Fact] - public async Task NoDesignerOnSecondClass() + [Theory, CombinatorialData] + public async Task NoDesignerOnSecondClass(TestHost host) { await TestAsync( @"class Test1 { } [System.ComponentModel.DesignerCategory(""Form"")] -class Test2 { }", category: null); +class Test2 { }", category: null, host); } - [Fact] - public async Task NoDesignerOnStruct() + [Theory, CombinatorialData] + public async Task NoDesignerOnStruct(TestHost host) { await TestAsync( @" [System.ComponentModel.DesignerCategory(""Form"")] -struct Test1 { }", category: null); +struct Test1 { }", category: null, host); } - [Fact] - public async Task NoDesignerOnNestedClass() + [Theory, CombinatorialData] + public async Task NoDesignerOnNestedClass(TestHost host) { await TestAsync( @@ -57,42 +61,56 @@ await TestAsync( { [System.ComponentModel.DesignerCategory(""Form"")] class Test2 { } -}", category: null); +}", category: null, host); } - [Fact] - public async Task SimpleDesignerTest() + [Theory, CombinatorialData] + public async Task SimpleDesignerTest(TestHost host) { await TestAsync( @"[System.ComponentModel.DesignerCategory(""Form"")] -class Test { }", "Form"); +class Test { }", "Form", host); } - [Fact] - public async Task SimpleDesignerTest2() + [Theory, CombinatorialData] + public async Task SimpleDesignerTest2(TestHost host) { await TestAsync( @"using System.ComponentModel; [DesignerCategory(""Form"")] -class Test { }", "Form"); +class Test { }", "Form", host); } - private static async Task TestAsync(string codeWithMarker, string category) + private static async Task TestAsync(string codeWithMarker, string? category, TestHost host) { - using var workspace = TestWorkspace.CreateCSharp(codeWithMarker, openDocuments: false); + using var workspace = TestWorkspace.CreateCSharp( + codeWithMarker, openDocuments: false, composition: host == TestHost.OutOfProcess ? s_outOffProcessComposition : s_inProcessComposition); + + var solution = workspace.CurrentSolution; var hostDocument = workspace.Documents.First(); var documentId = hostDocument.Id; - var document = workspace.CurrentSolution.GetDocument(documentId); - - var compilation = await document.Project.GetCompilationAsync(); - var actual = await DesignerAttributeHelpers.ComputeDesignerAttributeCategoryAsync( - compilation.DesignerCategoryAttributeType(), document, CancellationToken.None); - Assert.Equal(category, actual); + var service = solution.Services.GetRequiredService(); + var stream = service.ProcessProjectAsync(solution.GetRequiredProject(documentId.ProjectId), priorityDocumentId: null, CancellationToken.None); + + var items = new List(); + await foreach (var item in stream) + items.Add(item); + + if (category != null) + { + Assert.Equal(1, items.Count); + Assert.Equal(category, items.Single().Category); + Assert.Equal(documentId, items.Single().DocumentId); + } + else + { + Assert.Empty(items); + } } } } diff --git a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTests.cs b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTests.cs index 830d613dfbb37..e1ed66fdeb0be 100644 --- a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTests.cs +++ b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTests.cs @@ -12,6 +12,7 @@ using Microsoft.VisualStudio.LanguageServices.DocumentOutline; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Roslyn.VisualStudio.CSharp.UnitTests.DocumentOutline { @@ -47,9 +48,13 @@ void r() {} }" ; + public DocumentOutlineTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + private async Task<(DocumentOutlineTestMocks mocks, DocumentSymbolDataModel model, ImmutableArray uiItems)> InitializeMocksAndDataModelAndUIItems(string testCode) { - using var mocks = await CreateMocksAsync(testCode); + await using var mocks = await CreateMocksAsync(testCode); var response = await DocumentOutlineHelper.DocumentSymbolsRequestAsync(mocks.TextBuffer, mocks.LanguageServiceBroker, mocks.FilePath, CancellationToken.None); AssertEx.NotNull(response.Value); diff --git a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs index 060a18bcda786..92b99e6c09c9c 100644 --- a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs +++ b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.UnitTests; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; @@ -24,7 +25,9 @@ using Moq; using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; +using Xunit.Abstractions; using static Roslyn.Test.Utilities.AbstractLanguageServerProtocolTests; +using IAsyncDisposable = System.IAsyncDisposable; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Roslyn.VisualStudio.CSharp.UnitTests.DocumentOutline @@ -32,16 +35,22 @@ namespace Roslyn.VisualStudio.CSharp.UnitTests.DocumentOutline [UseExportProvider] public abstract class DocumentOutlineTestsBase { - protected class DocumentOutlineTestMocks : IDisposable + private readonly TestOutputLspLogger _logger; + protected DocumentOutlineTestsBase(ITestOutputHelper testOutputHelper) + { + _logger = new TestOutputLspLogger(testOutputHelper); + } + + protected class DocumentOutlineTestMocks : IAsyncDisposable { private readonly TestWorkspace _workspace; - private readonly IDisposable _disposable; + private readonly IAsyncDisposable _disposable; internal DocumentOutlineTestMocks( ILanguageServiceBroker2 languageServiceBroker, IThreadingContext threadingContext, TestWorkspace workspace, - IDisposable disposable) + IAsyncDisposable disposable) { LanguageServiceBroker = languageServiceBroker; ThreadingContext = threadingContext; @@ -59,14 +68,13 @@ internal DocumentOutlineTestMocks( internal string FilePath => "C:\\" + _workspace.Documents.Single().FilePath!; - public void Dispose() - => _disposable.Dispose(); + public ValueTask DisposeAsync() + => _disposable.DisposeAsync(); } private static readonly TestComposition s_composition = EditorTestCompositions.LanguageServerProtocol .AddParts(typeof(TestDocumentTrackingService)) .AddParts(typeof(TestWorkspaceRegistrationService)) - .AddParts(typeof(TestWorkspaceConfigurationService)) .RemoveParts(typeof(MockWorkspaceEventListenerProvider)); protected async Task CreateMocksAsync(string code) @@ -104,7 +112,7 @@ protected async Task CreateMocksAsync(string code) } } - private static async Task CreateTestLspServerAsync(TestWorkspace workspace, InitializationOptions initializationOptions) + private async Task CreateTestLspServerAsync(TestWorkspace workspace, InitializationOptions initializationOptions) { var solution = workspace.CurrentSolution; @@ -135,7 +143,7 @@ private static async Task CreateTestLspServerAsync(TestWorkspace var workspaceWaiter = operations.GetWaiter(FeatureAttribute.Workspace); await workspaceWaiter.ExpeditedWaitAsync(); - return await TestLspServer.CreateAsync(workspace, initializationOptions); + return await TestLspServer.CreateAsync(workspace, initializationOptions, _logger); } } } 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); + } } } diff --git a/src/VisualStudio/CodeLens/Microsoft.VisualStudio.LanguageServices.CodeLens.csproj b/src/VisualStudio/CodeLens/Microsoft.VisualStudio.LanguageServices.CodeLens.csproj index af6148f69c9cc..b81e6c5827b82 100644 --- a/src/VisualStudio/CodeLens/Microsoft.VisualStudio.LanguageServices.CodeLens.csproj +++ b/src/VisualStudio/CodeLens/Microsoft.VisualStudio.LanguageServices.CodeLens.csproj @@ -8,11 +8,7 @@ Microsoft.VisualStudio.LanguageServices.CodeLens net472 true - - false - - .NET Compiler Platform ("Roslyn") support for Visual Studio Code Lens. - + true diff --git a/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs b/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs index b5f9c55fbb18f..ed8d026e95bfb 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs @@ -17,7 +17,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddThisOrMeQualificationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddThisOrMeQualificationDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddThisOrMeQualificationDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? AddQualificationDiagnosticId; @@ -26,7 +26,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveThisOrMeQualificationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveThisOrMeQualificationDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveThisOrMeQualificationDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? RemoveQualificationDiagnosticId; @@ -35,7 +35,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] [Order(After = IDEDiagnosticIds.AddBracesDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_accessibility_modifiers))] public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; @@ -44,7 +44,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] public static readonly FixIdDefinition? OrderModifiersDiagnosticId; @@ -53,7 +53,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] [Order(After = IDEDiagnosticIds.AddThisOrMeQualificationDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Make_field_readonly))] public static readonly FixIdDefinition? MakeFieldReadonlyDiagnosticId; @@ -62,7 +62,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [Order(After = IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_casts))] public static readonly FixIdDefinition? RemoveUnnecessaryCastDiagnosticId; @@ -71,7 +71,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseObjectInitializerDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseObjectInitializerDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; @@ -80,7 +80,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCollectionInitializerDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCollectionInitializerDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] public static readonly FixIdDefinition? UseCollectionInitializerDiagnosticId; @@ -88,7 +88,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [FixId(AbstractCodeCleanUpFixer.FormatDocumentFixId)] [Name(AbstractCodeCleanUpFixer.FormatDocumentFixId)] [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-formatting")] + [HelpLink("https://learn.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-formatting")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Format_document))] public static readonly FixIdDefinition? FormatDocument; @@ -98,7 +98,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] + [HelpLink("https://learn.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_Imports_or_usings))] public static readonly FixIdDefinition? RemoveUnusedImports; @@ -108,7 +108,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] + [HelpLink("https://learn.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Sort_Imports_or_usings))] public static readonly FixIdDefinition? SortImports; @@ -118,7 +118,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.FileHeaderMismatch)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.FileHeaderMismatch}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.FileHeaderMismatch}")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] public static readonly FixIdDefinition? FileHeaderMismatch; @@ -128,7 +128,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_using_directive_placement_preferences))] public static readonly FixIdDefinition? MoveMisplacedUsingDirectivesDiagnosticId; @@ -137,7 +137,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] public static readonly FixIdDefinition? AddRequiredParenthesesDiagnosticId; @@ -155,7 +155,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] public static readonly FixIdDefinition? ExpressionValueIsUnusedDiagnosticId; @@ -182,7 +182,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_language_framework_type_preferences))] public static readonly FixIdDefinition? PreferBuiltInOrFrameworkTypeDiagnosticId; @@ -191,7 +191,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] public static readonly FixIdDefinition? RemoveUnnecessaryParenthesesDiagnosticId; @@ -200,7 +200,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_suppressions))] public static readonly FixIdDefinition? RemoveUnnecessarySuppressionDiagnosticId; @@ -209,7 +209,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_simplify_boolean_expression_preferences))] public static readonly FixIdDefinition? SimplifyConditionalExpressionDiagnosticId; @@ -218,7 +218,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyInterpolationId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyInterpolationId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyInterpolationId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_string_interpolation_preferences))] public static readonly FixIdDefinition? SimplifyInterpolationId; @@ -227,7 +227,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UnusedParameterDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UnusedParameterDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UnusedParameterDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_parameters))] public static readonly FixIdDefinition? UnusedParameterDiagnosticId; @@ -236,7 +236,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseAutoPropertyDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseAutoPropertyDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseAutoPropertyDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_auto_property_preferences))] public static readonly FixIdDefinition? UseAutoPropertyDiagnosticId; @@ -245,7 +245,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] public static readonly FixIdDefinition? UseCoalesceCompoundAssignmentDiagnosticId; @@ -254,7 +254,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_coalesce_expression_preferences))] public static readonly FixIdDefinition? UseCoalesceExpressionDiagnosticId; @@ -263,7 +263,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] public static readonly FixIdDefinition? UseCompoundAssignmentDiagnosticId; @@ -272,7 +272,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] public static readonly FixIdDefinition? UseConditionalExpressionForAssignmentDiagnosticId; @@ -281,7 +281,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] public static readonly FixIdDefinition? UseConditionalExpressionForReturnDiagnosticId; @@ -290,7 +290,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_tuple_name_preferences))] public static readonly FixIdDefinition? UseExplicitTupleNameDiagnosticId; @@ -299,7 +299,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseInferredMemberNameDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseInferredMemberNameDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseInferredMemberNameDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences))] public static readonly FixIdDefinition? UseInferredMemberNameDiagnosticId; @@ -308,7 +308,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseIsNullCheckDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIsNullCheckDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIsNullCheckDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_checking_preferences))] public static readonly FixIdDefinition? UseIsNullCheckDiagnosticId; @@ -317,7 +317,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNullPropagationDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullPropagationDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullPropagationDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_propagation_preferences))] public static readonly FixIdDefinition? UseNullPropagationDiagnosticId; @@ -326,7 +326,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] public static readonly FixIdDefinition? ValueAssignedIsUnusedDiagnosticId; diff --git a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs index 3baaad482db57..ca152c0f7b3ae 100644 --- a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs +++ b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs @@ -14,11 +14,6 @@ internal sealed class DebuggerIntelliSenseWorkspace : Workspace public DebuggerIntelliSenseWorkspace(Solution solution) : base(solution.Workspace.Services.HostServices, WorkspaceKind.Debugger) { - // The solution we are handed is still parented by the original workspace. We want to - // inherit it's "no partial solutions" flag so that way this workspace will also act - // deterministically if we're in unit tests - TestHookPartialSolutionsDisabled = solution.Workspace.TestHookPartialSolutionsDisabled; - SetCurrentSolution(solution); } diff --git a/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs b/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs index 524739b071b94..395b697978e3f 100644 --- a/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs +++ b/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs @@ -5,24 +5,24 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; +using EnvDTE; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.DesignerAttribute; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Designer.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; +using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Services; using Roslyn.Utilities; @@ -31,7 +31,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribu { [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] internal class VisualStudioDesignerAttributeService : - ForegroundThreadAffinitizedObject, IDesignerAttributeDiscoveryService.ICallback, IEventListener, IDisposable + ForegroundThreadAffinitizedObject, IEventListener, IDisposable { private readonly VisualStudioWorkspaceImpl _workspace; @@ -52,11 +52,37 @@ internal class VisualStudioDesignerAttributeService : /// private IVSMDDesignerService? _legacyDesignerService; + /// + /// Queue that tells us to recompute designer attributes when workspace changes happen. This queue is + /// cancellable and will be restarted when new changes come in. That way we can be quickly update the designer + /// attribute for a file when a user edits it. + /// private readonly AsyncBatchingWorkQueue _workQueue; - // We'll get notifications from the OOP server about new attribute arguments. Collect those notifications and - // deliver them to VS in batches to prevent flooding the UI thread. - private readonly AsyncBatchingWorkQueue _projectSystemNotificationQueue; + /// + /// We'll get notifications from the OOP server about new attribute arguments. Collect those notifications and + /// deliver them to VS in batches to prevent flooding the UI thread. Importantly, we do not cancel this queue. + /// Once we've decided to update the project system, we want to allow that to proceed. + /// + /// This queue both sends the individual data objects we get back, or the solution instance once we're done with + /// a particular project request. The latter is used so that we can determine which documents are now gone, so + /// we can dump our cached data for them. + /// + /// + private readonly AsyncBatchingWorkQueue<(CodeAnalysis.Solution? solution, DesignerAttributeData? data)> _projectSystemNotificationQueue; + + /// + /// Keep track of the last version we were at when we processed a project. We'll skip reprocessing projects if + /// that version hasn't changed. + /// + private readonly ConcurrentDictionary _projectToLastComputedDependentSemanticVersion = new(); + + /// + /// Keep track of the last information we reported per document. We will avoid notifying the host if we + /// recompute and these don't change. Note: we keep track if we reported as well to + /// represent that the file is not designable. + /// + private readonly ConcurrentDictionary _documentToLastReportedCategory = new(); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -78,7 +104,7 @@ public VisualStudioDesignerAttributeService( listener, ThreadingContext.DisposalToken); - _projectSystemNotificationQueue = new AsyncBatchingWorkQueue( + _projectSystemNotificationQueue = new AsyncBatchingWorkQueue<(CodeAnalysis.Solution? solution, DesignerAttributeData? data)>( TimeSpan.FromSeconds(1), this.NotifyProjectSystemAsync, listener, @@ -95,49 +121,135 @@ void IEventListener.StartListening(Workspace workspace, object _) if (workspace != _workspace) return; + // Register for changes, and kick off hte initial scan. _workspace.WorkspaceChanged += OnWorkspaceChanged; _workQueue.AddWork(cancelExistingWork: true); } private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) { + // cancel any existing work and start rescanning. this way we can respond to edits to a file very quickly. _workQueue.AddWork(cancelExistingWork: true); } private async ValueTask ProcessWorkspaceChangeAsync(CancellationToken cancellationToken) { + var statusService = _workspace.Services.GetRequiredService(); + await statusService.WaitUntilFullyLoadedAsync(cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); + if (client == null) + return; + var solution = _workspace.CurrentSolution; + + // remove any data for projects that are no longer around. + HandRemovedProjects(solution); + + // Now process all the projects we do have, prioritizing the active project/document first. + var trackingService = _workspace.Services.GetRequiredService(); + + var priorityDocument = solution.GetDocument(trackingService.TryGetActiveDocument()); + if (priorityDocument != null) + await ProcessProjectAsync(client, priorityDocument.Project, priorityDocument.Id, cancellationToken).ConfigureAwait(false); + + // Process the rest of the projects in dependency order so that their data is ready when we hit the + // projects that depend on them. + var dependencyGraph = solution.GetProjectDependencyGraph(); + foreach (var projectId in dependencyGraph.GetTopologicallySortedProjects(cancellationToken)) + { + // skip the prioritized project we handled above. + if (projectId == priorityDocument?.Id.ProjectId) + continue; + + await ProcessProjectAsync(client, solution.GetRequiredProject(projectId), priorityDocumentId: null, cancellationToken).ConfigureAwait(false); + } + } + + private void HandRemovedProjects(CodeAnalysis.Solution solution) + { foreach (var (projectId, _) in _cpsProjects) { if (!solution.ContainsProject(projectId)) _cpsProjects.TryRemove(projectId, out _); } - var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); - if (client == null) - return; + // when a project is removed, remove our cached attribute data for it. No point in actually notifying the + // host as there isn't any project anymore to notify it about. - var trackingService = _workspace.Services.GetRequiredService(); - var priorityDocument = trackingService.TryGetActiveDocument(); + foreach (var (projectId, _) in _projectToLastComputedDependentSemanticVersion) + { + if (!solution.ContainsProject(projectId)) + _projectToLastComputedDependentSemanticVersion.TryRemove(projectId, out _); + } + } - await client.TryInvokeAsync( - solution, - (service, checksum, callbackId, cancellationToken) => service.DiscoverDesignerAttributesAsync(callbackId, checksum, priorityDocument, cancellationToken), - callbackTarget: this, - cancellationToken).ConfigureAwait(false); + private async Task ProcessProjectAsync( + RemoteHostClient client, + CodeAnalysis.Project project, + DocumentId? priorityDocumentId, + CancellationToken cancellationToken) + { + // We need to recompute the designer attributes for a project if it's own semantic-version changes, or the + // semantic-version of any dependent projects change. The reason for checking dependent projects is that we + // look for the designer attribute on subclasses as well (so we have to walk the inheritance tree). This + // tree may be unfortunately be affected by dependent projects. In an ideal design we would require the + // attribute be on the declaration point so we could only check things that are known to directly have an + // attribute on them, and we wouldn't have to look up the inheritance hierarchy. + var dependentSemanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + if (!_projectToLastComputedDependentSemanticVersion.TryGetValue(project.Id, out var lastComputedVersion) || + lastComputedVersion != dependentSemanticVersion) + { + var stream = client.TryInvokeStreamAsync( + project, + (service, checksum, cancellationToken) => service.DiscoverDesignerAttributesAsync(checksum, project.Id, priorityDocumentId, cancellationToken), + cancellationToken); + + using var _ = PooledHashSet.GetInstance(out var seenDocuments); + + // get the results and add all the documents we hear about to the notification queue so they can be batched up. + await foreach (var data in stream.ConfigureAwait(false)) + { + seenDocuments.Add(data.DocumentId); + _projectSystemNotificationQueue.AddWork((solution: null, data)); + } + + // Also, for any documents we didn't hear about, ensure we emit a clear on its category if we have + // currently stored for it. This also ensures we initially report about all non-designer files when a + // solution is opened. This is needed in case the project file says it is designable, but the user made + // some change outside of VS that makes it non-designable. We will pick this up here and ensure the data + // is cleared. From that point on, we won't issue any more notifications about those files unless the + // category actually does change. + foreach (var document in project.Documents) + { + if (document.FilePath != null && !seenDocuments.Contains(document.Id)) + _projectSystemNotificationQueue.AddWork((solution: null, new DesignerAttributeData(null, document.Id, document.FilePath))); + } + + // once done, also enqueue the solution as well so that the project-system queue can cleanup + // any stale data about it. + _projectSystemNotificationQueue.AddWork((project.Solution, data: null)); + + // now that we're done processing the project, record this version-stamp so we don't have to process it again in the future. + _projectToLastComputedDependentSemanticVersion[project.Id] = dependentSemanticVersion; + } } private async ValueTask NotifyProjectSystemAsync( - ImmutableSegmentedList data, CancellationToken cancellationToken) + ImmutableSegmentedList<(CodeAnalysis.Solution? solution, DesignerAttributeData? data)> dataList, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - using var _1 = ArrayBuilder.GetInstance(out var filteredInfos); - AddFilteredInfos(data, filteredInfos); + using var _1 = ArrayBuilder.GetInstance(out var changedData); + using var _2 = ArrayBuilder.GetInstance(out var tasks); + + var latestSolution = AddChangedData(dataList, changedData); // Now, group all the notifications by project and update all the projects in parallel. - using var _2 = ArrayBuilder.GetInstance(out var tasks); - foreach (var group in filteredInfos.GroupBy(a => a.DocumentId.ProjectId)) + foreach (var group in changedData.GroupBy(a => a.DocumentId.ProjectId)) { cancellationToken.ThrowIfCancellationRequested(); tasks.Add(NotifyProjectSystemAsync(group.Key, group, cancellationToken)); @@ -145,40 +257,78 @@ private async ValueTask NotifyProjectSystemAsync( // Wait until all project updates have happened before processing the next batch. await Task.WhenAll(tasks).ConfigureAwait(false); + + // now that we've reported this data, record that we've done so so that we don't report the same data again in the future. + foreach (var data in changedData) + _documentToLastReportedCategory[data.DocumentId] = data.Category; + + // Now, check the documents we've stored against the changed projects to see if they're no longer around. If + // so, dump what we have. No need to notify anyone about this as the files are literally not in the + // solution anymore. this is just to ensure we don't hold onto data forever. + if (latestSolution != null) + { + foreach (var (documentId, _) in _documentToLastReportedCategory) + { + if (!latestSolution.ContainsDocument(documentId)) + _documentToLastReportedCategory.TryRemove(documentId, out _); + } + } } - private static void AddFilteredInfos(ImmutableSegmentedList data, ArrayBuilder filteredData) + private CodeAnalysis.Solution? AddChangedData( + ImmutableSegmentedList<(CodeAnalysis.Solution? solution, DesignerAttributeData? data)> dataList, + ArrayBuilder changedData) { - using var _ = PooledHashSet.GetInstance(out var seenDocumentIds); + using var _1 = PooledHashSet.GetInstance(out var seenDocumentIds); + using var _2 = ArrayBuilder.GetInstance(out var latestData); + + CodeAnalysis.Solution? lastSolution = null; + + for (var i = dataList.Count - 1; i >= 0; i--) + { + // go in reverse order so that results about the same document only take the later value. + var (solution, data) = dataList[i]; + + if (data != null) + { + if (seenDocumentIds.Add(data.Value.DocumentId)) + latestData.Add(data.Value); + } + + lastSolution ??= solution; + } - // Walk the list of designer items in reverse, and skip any items for a project once - // we've already seen it once. That way, we're only reporting the most up to date - // information for a project, and we're skipping the stale information. - for (var i = data.Count - 1; i >= 0; i--) + foreach (var data in latestData) { - var info = data[i]; - if (seenDocumentIds.Add(info.DocumentId)) - filteredData.Add(info); + // only issue a change notification for files we haven't issued a notification for, or for files that + // changed their category. + if (!_documentToLastReportedCategory.TryGetValue(data.DocumentId, out var existingCategory) || + existingCategory != data.Category) + { + changedData.Add(data); + } } + + return lastSolution; } private async Task NotifyProjectSystemAsync( ProjectId projectId, - IEnumerable data, + IEnumerable dataList, CancellationToken cancellationToken) { // Delegate to the CPS or legacy notification services as necessary. var cpsUpdateService = await GetUpdateServiceIfCpsProjectAsync(projectId, cancellationToken).ConfigureAwait(false); var task = cpsUpdateService == null - ? NotifyLegacyProjectSystemAsync(projectId, data, cancellationToken) - : NotifyCpsProjectSystemAsync(projectId, cpsUpdateService, data, cancellationToken); + ? NotifyLegacyProjectSystemAsync(projectId, dataList, cancellationToken) + : NotifyCpsProjectSystemAsync(projectId, cpsUpdateService, dataList, cancellationToken); await task.ConfigureAwait(false); } private async Task NotifyLegacyProjectSystemAsync( ProjectId projectId, - IEnumerable data, + IEnumerable dataList, CancellationToken cancellationToken) { // legacy project system can only be talked to on the UI thread. @@ -194,10 +344,10 @@ private async Task NotifyLegacyProjectSystemAsync( if (hierarchy == null) return; - foreach (var info in data) + foreach (var data in dataList) { cancellationToken.ThrowIfCancellationRequested(); - NotifyLegacyProjectSystemOnUIThread(designerService, hierarchy, info); + NotifyLegacyProjectSystemOnUIThread(designerService, hierarchy, data); } } @@ -239,7 +389,7 @@ private void NotifyLegacyProjectSystemOnUIThread( private async Task NotifyCpsProjectSystemAsync( ProjectId projectId, IProjectItemDesignerTypeUpdateService updateService, - IEnumerable data, + IEnumerable dataList, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -254,10 +404,10 @@ private async Task NotifyCpsProjectSystemAsync( using var _ = ArrayBuilder.GetInstance(out var tasks); - foreach (var info in data) + foreach (var data in dataList) { cancellationToken.ThrowIfCancellationRequested(); - tasks.Add(NotifyCpsProjectSystemAsync(updateService, info, cancellationToken)); + tasks.Add(NotifyCpsProjectSystemAsync(updateService, data, cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); @@ -312,15 +462,5 @@ private static async Task NotifyCpsProjectSystemAsync( return serviceProvider.GetService(typeof(IProjectItemDesignerTypeUpdateService)) as IProjectItemDesignerTypeUpdateService; } } - - /// - /// Callback from the OOP service back into us. - /// - public ValueTask ReportDesignerAttributeDataAsync(ImmutableArray data, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_projectSystemNotificationQueue); - _projectSystemNotificationQueue.AddWork(data); - return ValueTaskFactory.CompletedTask; - } } } diff --git a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs index f7ba92198a7ca..f846f4c9e42c1 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs @@ -64,7 +64,7 @@ private async Task InitializeWorkspaceAsync(ISolutionAnalyzerSetterWorkspaceServ } catch (Exception e) when (FatalError.ReportAndPropagate(e, ErrorSeverity.Diagnostic)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/VisualStudio/Core/Def/EditorConfigSettings/SettingsEditorControl.xaml.cs b/src/VisualStudio/Core/Def/EditorConfigSettings/SettingsEditorControl.xaml.cs index 176a143efd697..0a169a069fd1e 100644 --- a/src/VisualStudio/Core/Def/EditorConfigSettings/SettingsEditorControl.xaml.cs +++ b/src/VisualStudio/Core/Def/EditorConfigSettings/SettingsEditorControl.xaml.cs @@ -150,7 +150,7 @@ ContentPresenter GetTabItem(object tag) return AnalyzersFrame; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptTodoCommentService.cs b/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptTodoCommentService.cs deleted file mode 100644 index c34a38711d582..0000000000000 --- a/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptTodoCommentService.cs +++ /dev/null @@ -1,29 +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. - -#nullable disable - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.TodoComments; - -namespace Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api -{ - internal interface IVsTypeScriptTodoCommentService - { - /// - /// Legacy entry-point to allow existing in-process TypeScript language service to report todo comments. - /// TypeScript is responsible for determining when to compute todo comments (for example, on ). This can be called on any thread. - /// - [Obsolete("Use {nameof(ReportTaskListItemsAsync)} instead.")] - Task ReportTodoCommentsAsync(Document document, ImmutableArray todoComments, CancellationToken cancellationToken); - - Task ReportTaskListItemsAsync(Document document, ImmutableArray items, CancellationToken cancellationToken); - } -} diff --git a/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index 10d22bde1c75d..3c9779ec61a06 100644 --- a/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -345,7 +345,7 @@ public sealed override ValueTask OnDefinitionFoundAsync(DefinitionItem definitio } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -422,7 +422,7 @@ public sealed override async ValueTask OnReferenceFoundAsync(SourceReferenceItem } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 66dd83152b58e..eeec5768a2007 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,14 @@ 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(solution.Services, filePath, defaultEncoding: null); + 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/Implementation/AbstractVsTextViewFilter.cs b/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs index 6bdd65a9a50e1..07238c1754ebf 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs @@ -53,7 +53,7 @@ int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText) } catch (Exception e) when (FatalError.ReportAndCatch(e) && false) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -156,7 +156,7 @@ int IVsTextViewFilter.GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan) } catch (Exception e) when (FatalError.ReportAndCatch(e) && false) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTag.cs b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTag.cs index 1258e927d9459..4730567f14875 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTag.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTag.cs @@ -16,7 +16,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin { - internal class InheritanceMarginTag : IGlyphTag + internal class InheritanceMarginTag : IGlyphTag, IEquatable { /// /// Margin moniker. @@ -54,5 +54,20 @@ public InheritanceMarginTag(int lineNumber, ImmutableArray throw ExceptionUtilities.Unreachable(); + + public override bool Equals(object? obj) + => Equals(obj as InheritanceMarginTag); + + public bool Equals(InheritanceMarginTag? other) + { + return other != null && + this.LineNumber == other.LineNumber && + this.Moniker.Guid == other.Moniker.Guid && + this.Moniker.Id == other.Moniker.Id && + this.MembersOnLine.SequenceEqual(other.MembersOnLine); + } } } diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs index 7fe19eecad62f..82ed1a44c937a 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs @@ -150,5 +150,8 @@ protected override async Task ProduceTagsAsync( new InheritanceMarginTag(lineNumber, membersOnTheLineArray))); } } + + protected override bool TagEquals(InheritanceMarginTag tag1, InheritanceMarginTag tag2) + => tag1.Equals(tag2); } } diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs b/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs index 08e7de4507dcf..3c134e9dda6f1 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs @@ -39,7 +39,7 @@ public override DataTemplate SelectTemplate(object item, ItemsControl parentItem return (DataTemplate)parentItemsControl.FindResource("MemberMenuItemTemplate"); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs b/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs index 17d98114c523a..5d01267fe1d19 100644 --- a/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs +++ b/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs @@ -103,7 +103,7 @@ public void Create(int instanceId) InteractiveHostPlatform.Desktop64 => " (.NET Framework " + ServicesVSResources.Bitness64 + ")", InteractiveHostPlatform.Desktop32 => " (.NET Framework " + ServicesVSResources.Bitness32 + ")", InteractiveHostPlatform.Core => " (.NET Core)", - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } diff --git a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs index d35664d71111e..c4cf998739911 100644 --- a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs +++ b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs @@ -4,13 +4,15 @@ using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.VisualStudio.LogHub; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageClient { - internal class LogHubLspLogger : ILspLogger + internal class LogHubLspLogger : ILspServiceLogger { private readonly TraceConfiguration _configuration; private readonly TraceSource _traceSource; @@ -36,7 +38,7 @@ public void Dispose() _configuration.Dispose(); } - public void TraceInformation(string message) + public void LogInformation(string message, params object[] @params) { // Explicitly call TraceEvent here instead of TraceInformation. // TraceInformation indirectly calls string.Format which throws if the message @@ -45,19 +47,29 @@ public void TraceInformation(string message) _traceSource.TraceEvent(TraceEventType.Information, id: 0, message); } - public void TraceWarning(string message) - => _traceSource.TraceEvent(TraceEventType.Warning, id: 0, message); + public void LogWarning(string message, params object[] @params) + { + _traceSource.TraceEvent(TraceEventType.Warning, id: 0, message); + } - public void TraceError(string message) - => _traceSource.TraceEvent(TraceEventType.Error, id: 0, message); + public void LogError(string message, params object[] @params) + { + _traceSource.TraceEvent(TraceEventType.Error, id: 0, message); + } - public void TraceException(Exception exception) - => _traceSource.TraceEvent(TraceEventType.Error, id: 0, "Exception: {0}", exception); + public void LogException(Exception exception, string? message = null, params object[] @params) + { + _traceSource.TraceEvent(TraceEventType.Error, id: 0, "Exception: {0}", exception); + } - public void TraceStart(string message) - => _traceSource.TraceEvent(TraceEventType.Start, id: 0, message); + public void LogStartContext(string message, params object[] @params) + { + _traceSource.TraceEvent(TraceEventType.Start, id: 0, message); + } - public void TraceStop(string message) - => _traceSource.TraceEvent(TraceEventType.Stop, id: 0, message); + public void LogEndContext(string message, params object[] @params) + { + _traceSource.TraceEvent(TraceEventType.Stop, id: 0, message); + } } } diff --git a/src/VisualStudio/Core/Def/LanguageClient/VisualStudioLogHubLoggerFactory.cs b/src/VisualStudio/Core/Def/LanguageClient/VisualStudioLogHubLoggerFactory.cs index 721b71e1500c4..8bef6ee54258b 100644 --- a/src/VisualStudio/Core/Def/LanguageClient/VisualStudioLogHubLoggerFactory.cs +++ b/src/VisualStudio/Core/Def/LanguageClient/VisualStudioLogHubLoggerFactory.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.LogHub; using Microsoft.VisualStudio.RpcContracts.Logging; @@ -20,8 +21,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageClient { - [Export(typeof(ILspLoggerFactory))] - internal class VisualStudioLogHubLoggerFactory : ILspLoggerFactory + [Export(typeof(ILspServiceLoggerFactory))] + internal class VisualStudioLogHubLoggerFactory : ILspServiceLoggerFactory { /// /// A unique, always increasing, ID we use to identify this server in our loghub logs. Needed so that if our @@ -42,10 +43,10 @@ public VisualStudioLogHubLoggerFactory( _threadingContext = threadingContext; } - public async Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken) + public async Task CreateLoggerAsync(string serverTypeName, JsonRpc jsonRpc, CancellationToken cancellationToken) { var logName = $"Roslyn.{serverTypeName}.{Interlocked.Increment(ref s_logHubSessionId)}"; - var logId = new LogId(logName, new ServiceMoniker(typeof(LanguageServerTarget).FullName)); + var logId = new LogId(logName, new ServiceMoniker(typeof(AbstractLanguageServer<>).FullName)); var serviceContainer = await _asyncServiceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory).ConfigureAwait(false); var service = serviceContainer.GetFullAccessServiceBroker(); diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs index 6ba0691738bed..f0b0ab0fb13b2 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs @@ -2,9 +2,13 @@ // 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.ObjectModel; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; @@ -14,7 +18,6 @@ using Microsoft.CodeAnalysis.Snippets; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService @@ -25,15 +28,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService /// internal abstract class AbstractCreateServicesOnTextViewConnection : IWpfTextViewConnectionListener { - private readonly IAsynchronousOperationListener _listener; - private readonly IThreadingContext _threadingContext; private readonly string _languageName; + private readonly AsyncBatchingWorkQueue _workQueue; private bool _initialized = false; protected VisualStudioWorkspace Workspace { get; } protected IGlobalOptionService GlobalOptions { get; } - protected virtual Task InitializeServiceForOpenedDocumentAsync(Document document) + protected virtual Task InitializeServiceForProjectWithOpenedDocumentAsync(Project project) => Task.CompletedTask; public AbstractCreateServicesOnTextViewConnection( @@ -45,22 +47,25 @@ public AbstractCreateServicesOnTextViewConnection( { Workspace = workspace; GlobalOptions = globalOptions; - - _listener = listenerProvider.GetListener(FeatureAttribute.Workspace); - _threadingContext = threadingContext; _languageName = languageName; - Workspace.DocumentOpened += InitializeServiceOnDocumentOpened; + _workQueue = new AsyncBatchingWorkQueue( + TimeSpan.FromSeconds(1), + BatchProcessProjectsWithOpenedDocumentAsync, + EqualityComparer.Default, + listenerProvider.GetListener(FeatureAttribute.CompletionSet), + threadingContext.DisposalToken); + + Workspace.DocumentOpened += QueueWorkOnDocumentOpened; } void IWpfTextViewConnectionListener.SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) { if (!_initialized) { - var token = _listener.BeginAsyncOperation(nameof(InitializeServicesAsync)); - InitializeServicesAsync().CompletesAsyncOperation(token); - _initialized = true; + // use `null` to trigger per VS session intialization task + _workQueue.AddWork((ProjectId?)null); } } @@ -68,33 +73,37 @@ void IWpfTextViewConnectionListener.SubjectBuffersDisconnected(IWpfTextView text { } - private void InitializeServiceOnDocumentOpened(object sender, DocumentEventArgs e) + private async ValueTask BatchProcessProjectsWithOpenedDocumentAsync(ImmutableSegmentedList projectIds, CancellationToken cancellationToken) { - if (e.Document.Project.Language != _languageName) + foreach (var projectId in projectIds) { - return; - } - - var token = _listener.BeginAsyncOperation(nameof(InitializeServiceForOpenedDocumentOnBackgroundAsync)); - InitializeServiceForOpenedDocumentOnBackgroundAsync(e.Document).CompletesAsyncOperation(token); - - async Task InitializeServiceForOpenedDocumentOnBackgroundAsync(Document document) - { - await TaskScheduler.Default; - - // Preload project completion providers on a background thread since loading extensions can be slow - // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1488945 - if (document.GetLanguageService() is not null) - _ = CompletionService.GetProjectCompletionProviders(document.Project); - - await InitializeServiceForOpenedDocumentAsync(document).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + + if (projectId is null) + { + InitializePerVSSessionServices(); + } + else if (Workspace.CurrentSolution.GetProject(projectId) is Project project) + { + // Preload project completion providers at document open also helps avoid redundant file reads + // from a race caused by multiple features (codefix, refactoring, etc.) attempting to get extensions + // from analyzer references at the same time when they are not cached. + if (project.GetLanguageService() is CompletionService completionService) + completionService.TriggerLoadProjectProviders(project); + + await InitializeServiceForProjectWithOpenedDocumentAsync(project).ConfigureAwait(false); + } } } - private async Task InitializeServicesAsync() + private void QueueWorkOnDocumentOpened(object sender, DocumentEventArgs e) { - await TaskScheduler.Default; + if (e.Document.Project.Language == _languageName) + _workQueue.AddWork(e.Document.Project.Id); + } + private void InitializePerVSSessionServices() + { var languageServices = Workspace.Services.GetExtendedLanguageServices(_languageName); _ = languageServices.GetService(); diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs index be03307e56431..618c7518be45a 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs @@ -24,7 +24,7 @@ int IVsLanguageDebugInfo.GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCo } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -36,7 +36,7 @@ int IVsLanguageDebugInfo.GetLocationOfName(string pszName, out string pbstrMkDoc } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -48,7 +48,7 @@ int IVsLanguageDebugInfo.GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -60,7 +60,7 @@ int IVsLanguageDebugInfo.GetProximityExpressions(IVsTextBuffer pBuffer, int iLin } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -72,7 +72,7 @@ int IVsLanguageDebugInfo.IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -84,7 +84,7 @@ int IVsLanguageDebugInfo.ResolveName(string pszName, uint dwFlags, out IVsEnumDe } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -96,7 +96,7 @@ int IVsLanguageDebugInfo.ValidateBreakpointLocation(IVsTextBuffer pBuffer, int i } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.nuspec b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.nuspec index e3770c76783f8..0955ef9bd039c 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.nuspec +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.nuspec @@ -23,5 +23,6 @@ https://docs.microsoft.com/en-us/nuget/create-packages/select-assemblies-referenced-by-projects. --> + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index d9d7f207046f5..9f20dbb1c882b 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 @@ -45,17 +43,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)); + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) => TextAndVersion.Create(_text, VersionStamp.Create()); } } diff --git a/src/VisualStudio/Core/Def/Progression/GraphNodeIdCreation.cs b/src/VisualStudio/Core/Def/Progression/GraphNodeIdCreation.cs index ca41fc7188082..928e7e92527db 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNodeIdCreation.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNodeIdCreation.cs @@ -142,7 +142,7 @@ private static async Task GetPartialForTypeAsync(ITypeSymbol symbol return GetPartialForDynamicType(nodeName); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static GraphNodeId GetPartialForDynamicType(GraphNodeIdName nodeName) @@ -554,7 +554,7 @@ private static async Task GetLocalVariableIndexAsync(ISymbol symbol, Soluti } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs b/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs index 744779f300135..828c3a85d5654 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs @@ -181,7 +181,7 @@ private static async Task PopulateContextGraphAsync(Solution solution, List 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()) { @@ -401,7 +405,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))); } } } @@ -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); }); @@ -586,19 +581,16 @@ 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, - designTimeOnly: true, - documentServiceProvider: documentServiceProvider); + isGenerated: false) + .WithDesignTimeOnly(true) + .WithDocumentServiceProvider(fileInfo.DocumentServiceProvider); } private sealed class SourceTextLoader : TextLoader @@ -612,7 +604,7 @@ public SourceTextLoader(SourceTextContainer textContainer, string? filePath) _filePath = filePath; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); } } 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..6e48983529a3a 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; @@ -80,8 +81,15 @@ public async Task CreateAndAddToWorkspaceAsync( var vsixAnalyzerProvider = await _vsixAnalyzerProviderFactory.GetOrCreateProviderAsync(cancellationToken).ConfigureAwait(false); - // Following can be off the UI thread. - await TaskScheduler.Default; + // The rest of this method can be ran off the UI thread. We'll only switch though if the UI thread isn't already blocked -- the legacy project + // system creates project synchronously, and during solution load we've seen traces where the thread pool is sufficiently saturated that this + // switch can't be completed quickly. For the rest of this method, we won't use ConfigureAwait(false) since we're expecting VS threading + // rules to apply. +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task + if (!_threadingContext.JoinableTaskContext.IsMainThreadBlocked()) + { + await TaskScheduler.Default; + } // From this point on, we start mutating the solution. So make us non cancellable. cancellationToken = CancellationToken.None; @@ -111,15 +119,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. @@ -140,14 +151,16 @@ await _visualStudioWorkspaceImpl.ApplyChangeToWorkspaceAsync(w => { w.OnProjectAdded(projectInfo); } - }).ConfigureAwait(false); + }); // Ensure that other VS contexts get accurate information that the UIContext for this language is now active. // This is not cancellable as we have already mutated the solution. - await _visualStudioWorkspaceImpl.RefreshProjectExistsUIContextForLanguageAsync(language, CancellationToken.None).ConfigureAwait(false); + await _visualStudioWorkspaceImpl.RefreshProjectExistsUIContextForLanguageAsync(language, CancellationToken.None); return project; +#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task + static Guid GetSolutionSessionId() { var dataModelTelemetrySession = TelemetryService.DefaultSession; 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 9b28ed151b3e5..3d05831dbeede 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs @@ -10,6 +10,8 @@ using System.Threading.Tasks; 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; @@ -317,18 +319,20 @@ private void TryClosingDocumentsForMoniker(string moniker) { if (w.IsDocumentOpen(documentId) && !_workspace._documentsNotFromFiles.Contains(documentId)) { - if (w.CurrentSolution.ContainsDocument(documentId)) + var solution = w.CurrentSolution; + + if (solution.GetDocument(documentId) is { } document) { - w.OnDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + w.OnDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } - else if (w.CurrentSolution.ContainsAdditionalDocument(documentId)) + else if (solution.GetAdditionalDocument(documentId) is { } additionalDocument) { - w.OnAdditionalDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + w.OnAdditionalDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } else { - Debug.Assert(w.CurrentSolution.ContainsAnalyzerConfigDocument(documentId)); - w.OnAnalyzerConfigDocumentClosed(documentId, new FileTextLoader(moniker, defaultEncoding: null)); + var analyzerConfigDocument = solution.GetRequiredAnalyzerConfigDocument(documentId); + w.OnAnalyzerConfigDocumentClosed(documentId, new WorkspaceFileTextLoader(w.Services.SolutionServices, moniker, defaultEncoding: null)); } } } diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerRoot.xaml b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerRoot.xaml index 3a0a90108fb79..d66c55ebb4018 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerRoot.xaml +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerRoot.xaml @@ -23,6 +23,7 @@ + @@ -108,14 +109,33 @@ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> - diff --git a/src/VisualStudio/Core/Def/TableDataSource/AbstractTableEntriesSnapshot.cs b/src/VisualStudio/Core/Def/TableDataSource/AbstractTableEntriesSnapshot.cs index 595e4b1e4e210..79fe40d48168f 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/AbstractTableEntriesSnapshot.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/AbstractTableEntriesSnapshot.cs @@ -2,12 +2,16 @@ // 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.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading; +using System.Windows; +using System.Windows.Controls; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Text; @@ -30,6 +34,7 @@ internal abstract class AbstractTableEntriesSnapshot : ITableEntriesSnaps private readonly int _version; private readonly ImmutableArray _items; private ImmutableArray _trackingPoints; + private FrameworkElement[]? _descriptions; protected AbstractTableEntriesSnapshot(IThreadingContext threadingContext, int version, ImmutableArray items, ImmutableArray trackingPoints) { @@ -228,5 +233,72 @@ public void StartCaching() public void StopCaching() { } + + protected static bool CanCreateDetailsContent(int index, Func getDiagnosticTableItem) + { + var item = getDiagnosticTableItem(index)?.Data; + if (item == null) + { + return false; + } + + return !string.IsNullOrWhiteSpace(item.Description); + } + + protected bool TryCreateDetailsContent(int index, Func getDiagnosticTableItem, [NotNullWhen(returnValue: true)] out FrameworkElement? expandedContent) + { + var item = getDiagnosticTableItem(index)?.Data; + if (item == null) + { + expandedContent = null; + return false; + } + + expandedContent = GetOrCreateTextBlock(ref _descriptions, this.Count, index, item, i => GetDescriptionTextBlock(i)); + return true; + } + + protected static bool TryCreateDetailsStringContent(int index, Func getDiagnosticTableItem, [NotNullWhen(returnValue: true)] out string? content) + { + var item = getDiagnosticTableItem(index)?.Data; + if (item == null) + { + content = null; + return false; + } + + if (string.IsNullOrWhiteSpace(item.Description)) + { + content = null; + return false; + } + + content = item.Description; + return content != null; + } + + private static FrameworkElement GetDescriptionTextBlock(DiagnosticData item) + { + return new TextBlock() + { + Background = null, + Padding = new Thickness(10, 6, 10, 8), + TextWrapping = TextWrapping.Wrap, + Text = item.Description + }; + } + + private static FrameworkElement GetOrCreateTextBlock( + [NotNull] ref FrameworkElement[]? caches, int count, int index, DiagnosticData item, Func elementCreator) + { + caches ??= new FrameworkElement[count]; + + if (caches[index] == null) + { + caches[index] = elementCreator(item); + } + + return caches[index]; + } } } diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListSuppressionStateService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListSuppressionStateService.cs index 478eca3bc3ad9..b1e641c3ab4e5 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListSuppressionStateService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListSuppressionStateService.cs @@ -197,15 +197,8 @@ private static bool IsNonRoslynEntrySupportingSuppressionState(ITableEntryHandle /// /// Returns true if an entry's suppression state can be modified. /// - /// private static bool IsEntryWithConfigurableSuppressionState([NotNullWhen(true)] DiagnosticData? entry) - { - // Compiler diagnostics with severity 'Error' are not configurable. - // Additionally, diagnostics coming from build are from a snapshot (as opposed to live diagnostics) and cannot be configured. - return entry != null && - !SuppressionHelpers.IsNotConfigurableDiagnostic(entry) && - !entry.IsBuildDiagnostic(); - } + => entry != null && !SuppressionHelpers.IsNotConfigurableDiagnostic(entry); private static AbstractTableEntriesSnapshot? GetEntriesSnapshot(ITableEntryHandle entryHandle, out int index) { diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 7193f1f128067..0f9c27422724d 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -576,7 +576,10 @@ private async Task>> Ge } // Filter out stale diagnostics in error list. - documentDiagnosticsToFix = documentDiagnostics.Value.Where(d => latestDocumentDiagnostics.Contains(d) || SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(d)); + documentDiagnosticsToFix = documentDiagnostics.Value + .Where(d => latestDocumentDiagnostics.Contains(d) || + d.IsBuildDiagnostic() || + SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(d)); } else { diff --git a/src/VisualStudio/Core/Def/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs b/src/VisualStudio/Core/Def/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs index 59ff6434df638..440f07171bbf5 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs @@ -327,7 +327,6 @@ public override ImmutableArray GetTrackingPoints(ImmutableArray< private class TableEntriesSnapshot : AbstractTableEntriesSnapshot, IWpfTableEntriesSnapshot { private readonly DiagnosticTableEntriesSource _source; - private FrameworkElement[]? _descriptions; public TableEntriesSnapshot( IThreadingContext threadingContext, @@ -491,71 +490,13 @@ public override bool TryNavigateTo(int index, NavigationOptions options, Cancell #region IWpfTableEntriesSnapshot public bool CanCreateDetailsContent(int index) - { - var item = GetItem(index)?.Data; - if (item == null) - { - return false; - } - - return !string.IsNullOrWhiteSpace(item.Description); - } + => CanCreateDetailsContent(index, GetItem); public bool TryCreateDetailsContent(int index, [NotNullWhen(returnValue: true)] out FrameworkElement? expandedContent) - { - var item = GetItem(index)?.Data; - if (item == null) - { - expandedContent = null; - return false; - } - - expandedContent = GetOrCreateTextBlock(ref _descriptions, this.Count, index, item, i => GetDescriptionTextBlock(i)); - return true; - } + => TryCreateDetailsContent(index, GetItem, out expandedContent); public bool TryCreateDetailsStringContent(int index, [NotNullWhen(returnValue: true)] out string? content) - { - var item = GetItem(index)?.Data; - if (item == null) - { - content = null; - return false; - } - - if (string.IsNullOrWhiteSpace(item.Description)) - { - content = null; - return false; - } - - content = item.Description; - return content != null; - } - - private static FrameworkElement GetDescriptionTextBlock(DiagnosticData item) - { - return new TextBlock() - { - Background = null, - Padding = new Thickness(10, 6, 10, 8), - TextWrapping = TextWrapping.Wrap, - Text = item.Description - }; - } - - private static FrameworkElement GetOrCreateTextBlock( - [NotNull] ref FrameworkElement[]? caches, int count, int index, DiagnosticData item, Func elementCreator) - { - caches ??= new FrameworkElement[count]; - - if (caches[index] == null) - { - caches[index] = elementCreator(item); - } - - return caches[index]; - } + => TryCreateDetailsStringContent(index, GetItem, out content); // unused ones public bool TryCreateColumnContent(int index, string columnName, bool singleColumnView, [NotNullWhen(returnValue: true)] out FrameworkElement? content) @@ -582,14 +523,6 @@ public bool TryCreateToolTip(int index, string columnName, [NotNullWhen(returnVa return false; } -#pragma warning disable IDE0060 // Remove unused parameter - TODO: remove this once we moved to new drop - public static bool TryCreateStringContent(int index, string columnName, bool singleColumnView, [NotNullWhen(returnValue: true)] out string? content) -#pragma warning restore IDE0060 // Remove unused parameter - { - content = null; - return false; - } - #endregion } diff --git a/src/VisualStudio/Core/Def/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs b/src/VisualStudio/Core/Def/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs index 436bb18e61f07..22c30796b22da 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs @@ -9,12 +9,16 @@ using System.IO; using System.Linq; using System.Threading; +using System.Windows; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; +using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; +using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -109,7 +113,7 @@ public TableEntriesSource(BuildTableDataSource source) public override object Key => _source._key; public override string BuildTool => PredefinedBuildTools.Build; public override bool SupportSpanTracking => false; - public override DocumentId TrackingDocumentId => throw ExceptionUtilities.Unreachable; + public override DocumentId TrackingDocumentId => throw ExceptionUtilities.Unreachable(); public override ImmutableArray GetItems() { @@ -122,7 +126,7 @@ public override ImmutableArray GetTrackingPoints(ImmutableArray< => ImmutableArray.Empty; } - private class TableEntriesSnapshot : AbstractTableEntriesSnapshot + private class TableEntriesSnapshot : AbstractTableEntriesSnapshot, IWpfTableEntriesSnapshot { private readonly DiagnosticTableEntriesSource _source; @@ -272,6 +276,44 @@ public override bool TryNavigateTo(int index, NavigationOptions options, Cancell // okay, there is no right one, take the first one if there is any return documentIds.FirstOrDefault(); } + + #region IWpfTableEntriesSnapshot + + public bool CanCreateDetailsContent(int index) + => CanCreateDetailsContent(index, GetItem); + + public bool TryCreateDetailsContent(int index, [NotNullWhen(returnValue: true)] out FrameworkElement? expandedContent) + => TryCreateDetailsContent(index, GetItem, out expandedContent); + + public bool TryCreateDetailsStringContent(int index, [NotNullWhen(returnValue: true)] out string? content) + => TryCreateDetailsStringContent(index, GetItem, out content); + + // unused ones + public bool TryCreateColumnContent(int index, string columnName, bool singleColumnView, [NotNullWhen(returnValue: true)] out FrameworkElement? content) + { + content = null; + return false; + } + + public bool TryCreateImageContent(int index, string columnName, bool singleColumnView, out ImageMoniker content) + { + content = default; + return false; + } + + public bool TryCreateStringContent(int index, string columnName, bool truncatedText, bool singleColumnView, [NotNullWhen(returnValue: true)] out string? content) + { + content = null; + return false; + } + + public bool TryCreateToolTip(int index, string columnName, [NotNullWhen(returnValue: true)] out object? toolTip) + { + toolTip = null; + return false; + } + + #endregion } } } diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 0618815e75342..c750260c9561e 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; @@ -40,6 +41,7 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou { private readonly Workspace _workspace; private readonly IDiagnosticAnalyzerService _diagnosticService; + private readonly IBuildOnlyDiagnosticsService _buildOnlyDiagnosticsService; private readonly IGlobalOperationNotificationService _notificationService; private readonly CancellationToken _disposalToken; @@ -101,10 +103,13 @@ internal ExternalErrorDiagnosticUpdateSource( _workspace.WorkspaceChanged += OnWorkspaceChanged; _diagnosticService = diagnosticService; + _buildOnlyDiagnosticsService = _workspace.Services.GetRequiredService(); _notificationService = _workspace.Services.GetRequiredService(); } + public DiagnosticAnalyzerInfoCache AnalyzerInfoCache => _diagnosticService.AnalyzerInfoCache; + /// /// Event generated from the serialized whenever the build progress in Visual Studio changes. /// Events are guaranteed to be generated in a serial fashion, but may be invoked on any thread. @@ -516,12 +521,14 @@ private InProgressState GetOrCreateInProgressState() private void RaiseDiagnosticsCreated(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray items) { + _buildOnlyDiagnosticsService.AddBuildOnlyDiagnostics(solution, projectId, documentId, items); DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( CreateArgumentKey(id), _workspace, solution, projectId, documentId, items)); } private void RaiseDiagnosticsRemoved(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId) { + _buildOnlyDiagnosticsService.ClearBuildOnlyDiagnostics(solution, projectId, documentId); DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsRemoved( CreateArgumentKey(id), _workspace, solution, projectId, documentId)); } diff --git a/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs b/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs index df64de187b3c9..653352eff0e5a 100644 --- a/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs +++ b/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs @@ -8,11 +8,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Extensions; @@ -41,6 +44,8 @@ public ProjectExternalErrorReporter(ProjectId projectId, string errorCodePrefix, { } + private DiagnosticAnalyzerInfoCache AnalyzerInfoCache => _workspace.ExternalErrorDiagnosticUpdateSource.AnalyzerInfoCache; + public ProjectExternalErrorReporter(ProjectId projectId, string errorCodePrefix, string language, VisualStudioWorkspaceImpl workspace) { Debug.Assert(projectId != null); @@ -99,11 +104,13 @@ public int AddNewErrors(IVsEnumExternalErrors pErrors) projectErrors.Add(GetDiagnosticData( documentId: null, _projectId, + _workspace, GetErrorId(error), error.bstrText, GetDiagnosticSeverity(error), _language, - new FileLinePositionSpan(project.FilePath ?? "", span: default))); + new FileLinePositionSpan(project.FilePath ?? "", span: default), + AnalyzerInfoCache)); } DiagnosticProvider.AddNewErrors(_projectId, projectErrors, documentErrorsMap); @@ -173,13 +180,15 @@ private DiagnosticData TryCreateDocumentDiagnosticItem(ExternalError error) return GetDiagnosticData( documentId, _projectId, + _workspace, GetErrorId(error), error.bstrText, GetDiagnosticSeverity(error), _language, new FileLinePositionSpan(error.bstrFileName, new LinePosition(line, column), - new LinePosition(line, column))); + new LinePosition(line, column)), + AnalyzerInfoCache); } public int ReportError(string bstrErrorMessage, string bstrErrorId, [ComAliasName("VsShell.VSTASKPRIORITY")] VSTASKPRIORITY nPriority, int iLine, int iColumn, string bstrFileName) @@ -227,6 +236,7 @@ public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasN var diagnostic = GetDiagnosticData( documentId, _projectId, + _workspace, bstrErrorId, bstrErrorMessage, severity, @@ -234,7 +244,8 @@ public void ReportError2(string bstrErrorMessage, string bstrErrorId, [ComAliasN new FileLinePositionSpan( bstrFileName, new LinePosition(iStartLine, iStartColumn), - new LinePosition(iEndLine, iEndColumn))); + new LinePosition(iEndLine, iEndColumn)), + AnalyzerInfoCache); if (documentId == null) { @@ -255,29 +266,71 @@ public int ClearErrors() private static DiagnosticData GetDiagnosticData( DocumentId documentId, ProjectId projectId, + Workspace workspace, string errorId, string message, DiagnosticSeverity severity, string language, - FileLinePositionSpan unmappedSpan) + FileLinePositionSpan unmappedSpan, + DiagnosticAnalyzerInfoCache analyzerInfoCache) { - return new DiagnosticData( + string title, description, category, helpLink; + DiagnosticSeverity defaultSeverity; + bool isEnabledByDefault; + ImmutableArray customTags; + + if (analyzerInfoCache != null && analyzerInfoCache.TryGetDescriptorForDiagnosticId(errorId, out var descriptor)) + { + title = descriptor.Title.ToString(CultureInfo.CurrentUICulture); + description = descriptor.Description.ToString(CultureInfo.CurrentUICulture); + category = descriptor.Category; + defaultSeverity = descriptor.DefaultSeverity; + isEnabledByDefault = descriptor.IsEnabledByDefault; + customTags = descriptor.CustomTags.AsImmutableOrEmpty(); + helpLink = descriptor.HelpLinkUri; + } + else + { + title = message; + description = message; + category = WellKnownDiagnosticTags.Build; + defaultSeverity = severity; + isEnabledByDefault = true; + customTags = IsCompilerDiagnostic(errorId) ? CompilerDiagnosticCustomTags : CustomTags; + helpLink = null; + } + + var diagnostic = new DiagnosticData( id: errorId, - category: WellKnownDiagnosticTags.Build, + category: category, message: message, - title: message, + title: title, + description: description, severity: severity, - defaultSeverity: severity, - isEnabledByDefault: true, + defaultSeverity: defaultSeverity, + isEnabledByDefault: isEnabledByDefault, warningLevel: (severity == DiagnosticSeverity.Error) ? 0 : 1, - customTags: IsCompilerDiagnostic(errorId) ? CompilerDiagnosticCustomTags : CustomTags, + customTags: customTags, properties: DiagnosticData.PropertiesForBuildDiagnostic, projectId: projectId, location: new DiagnosticDataLocation( unmappedSpan, documentId, mappedFileSpan: null), - language: language); + language: language, + helpLink: helpLink); + + if (workspace.CurrentSolution.GetDocument(documentId) is Document document && + document.SupportsSyntaxTree) + { + var tree = document.GetSyntaxTreeSynchronously(CancellationToken.None); + var text = tree.GetText(); + var span = diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text); + var location = diagnostic.DataLocation.WithSpan(span, tree); + return diagnostic.WithLocations(location, additionalLocations: default); + } + + return diagnostic; } private static bool IsCompilerDiagnostic(string errorId) diff --git a/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs b/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs index aa41eef8cd281..70819f274f888 100644 --- a/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs +++ b/src/VisualStudio/Core/Def/TaskList/VisualStudioTaskListService.cs @@ -18,21 +18,21 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.TodoComments; -using Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using TaskListItem = Microsoft.CodeAnalysis.TaskList.TaskListItem; namespace Microsoft.VisualStudio.LanguageServices.TaskList { - [Export(typeof(IVsTypeScriptTodoCommentService))] [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] internal class VisualStudioTaskListService : ITaskListProvider, - IVsTypeScriptTodoCommentService, IEventListener { private readonly IThreadingContext _threadingContext; private readonly VisualStudioWorkspaceImpl _workspace; + private readonly IAsyncServiceProvider _asyncServiceProvider; private readonly EventListenerTracker _eventListenerTracker; private readonly TaskListListener _listener; @@ -45,10 +45,12 @@ public VisualStudioTaskListService( VisualStudioWorkspaceImpl workspace, IGlobalOptionService globalOptions, IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + SVsServiceProvider asyncServiceProvider, [ImportMany] IEnumerable> eventListeners) { _threadingContext = threadingContext; _workspace = workspace; + _asyncServiceProvider = (IAsyncServiceProvider)asyncServiceProvider; _eventListenerTracker = new EventListenerTracker(eventListeners, WellKnownEventListeners.TaskListProvider); _listener = new TaskListListener( @@ -80,6 +82,12 @@ private async Task StartAsync(Workspace workspace) var workspaceStatus = workspace.Services.GetRequiredService(); await workspaceStatus.WaitUntilFullyLoadedAsync(_threadingContext.DisposalToken).ConfigureAwait(false); + // Wait until the task list is actually visible so that we don't perform pointless work analyzing files + // when the user would not even see the results. When we actually do register the analyer (in + // _listener.Start below), solution-crawler will reanalyze everything with this analayzer, so it will + // still find and present all the relevant items to the user. + await WaitUntilTaskListActivatedAsync().ConfigureAwait(false); + // Now that we've started, let the VS todo list know to start listening to us _eventListenerTracker.EnsureEventListener(_workspace, this); @@ -96,19 +104,35 @@ private async Task StartAsync(Workspace workspace) } } - /// - [Obsolete] - async Task IVsTypeScriptTodoCommentService.ReportTodoCommentsAsync( - Document document, ImmutableArray todoComments, CancellationToken cancellationToken) + private async Task WaitUntilTaskListActivatedAsync() { - var converted = await TodoComment.ConvertAsync(document, todoComments, cancellationToken).ConfigureAwait(false); + var cancellationToken = _threadingContext.DisposalToken; + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var taskList = await _asyncServiceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory).ConfigureAwait(true); - await _listener.ReportTaskListItemsAsync( - document.Id, converted, cancellationToken).ConfigureAwait(false); - } + var control = taskList.TableControl.Control; + + // if control is already visible, we can proceed to collect task list items. + if (control.IsVisible) + return; + + // otherwise, wait for it to become visible. + var taskSource = new TaskCompletionSource(); + control.IsVisibleChanged += Control_IsVisibleChanged; + + await taskSource.Task.ConfigureAwait(false); - async Task IVsTypeScriptTodoCommentService.ReportTaskListItemsAsync(Document document, ImmutableArray items, CancellationToken cancellationToken) - => await _listener.ReportTaskListItemsAsync(document.Id, items, cancellationToken).ConfigureAwait(false); + return; + + void Control_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) + { + if (control.IsVisible) + { + control.IsVisibleChanged -= Control_IsVisibleChanged; + taskSource.TrySetResult(true); + } + } + } public ImmutableArray GetTaskListItems(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) => _listener.GetTaskListItems(documentId); diff --git a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs index 79256bf75f754..a3ed457572062 100644 --- a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs +++ b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs @@ -28,7 +28,7 @@ public static ProjectSystemReferenceUpdate ToProjectSystemReferenceUpdate(this R UpdateAction.TreatAsUsed => ProjectSystemUpdateAction.SetTreatAsUsed, UpdateAction.TreatAsUnused => ProjectSystemUpdateAction.UnsetTreatAsUsed, UpdateAction.Remove => ProjectSystemUpdateAction.Remove, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; return new ProjectSystemReferenceUpdate( updateAction, diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs index 975341ee4aaf5..2843db4f7971e 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs @@ -178,7 +178,7 @@ private HostType GetHostType() } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public SourceTextContainer GetOpenTextContainer() @@ -587,7 +587,7 @@ private static bool TryGetSubTextChange( } // if it got hit, then it means there is a missing case - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private IHierarchicalDifferenceCollection DiffStrings(string leftTextWithReplacement, string rightTextWithReplacement) 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/Def/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs index 94a4ac734a9f3..94b5c7e2c5d92 100644 --- a/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs @@ -346,7 +346,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) else { // The file isn't there anymore; do we still have the generator at all? - if (project.AnalyzerReferences.Any(a => a.GetGenerators(project.Language).Any(static (g, self) => SourceGeneratorIdentity.GetGeneratorAssemblyName(g) == self._documentIdentity.Generator.AssemblyName, this))) + if (project.AnalyzerReferences.Any(a => a.FullPath == _documentIdentity.Generator.AssemblyPath)) { windowFrameMessageToShow = string.Format(ServicesVSResources.The_generator_0_that_generated_this_file_has_stopped_generating_this_file, GeneratorDisplayName); windowFrameImageMonikerToShow = KnownMonikers.StatusError; diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs index 6fff8051955df..aaf018f6fd340 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs @@ -91,12 +91,14 @@ public VisualStudioSymbolNavigationService( // See if there's another .Net language service that can handle navigating to this metadata symbol (for example, F#). var docCommentId = symbol.GetDocumentationCommentId(); - if (docCommentId != null) + var assemblyName = symbol.ContainingAssembly.Identity.Name; + if (docCommentId != null && assemblyName != null) { foreach (var lazyService in solution.Services.ExportProvider.GetExports()) { var crossLanguageService = lazyService.Value; - var crossLanguageLocation = await crossLanguageService.TryGetNavigableLocationAsync(docCommentId, cancellationToken).ConfigureAwait(false); + var crossLanguageLocation = await crossLanguageService.TryGetNavigableLocationAsync( + assemblyName, docCommentId, cancellationToken).ConfigureAwait(false); if (crossLanguageLocation != null) return crossLanguageLocation; } 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/Microsoft.VisualStudio.LanguageServices.Implementation.csproj b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj index 2263d60566acf..3e68235b2a447 100644 --- a/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj +++ b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj @@ -8,6 +8,7 @@ Microsoft.VisualStudio.LanguageServices.Implementation true full + true diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs index 4312434837b89..c85e0b0d1d632 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs @@ -212,7 +212,7 @@ internal SymbolKindOrTypeKind CreateSymbolOrTypeOrMethodKind() _symbolKind.HasValue ? new SymbolKindOrTypeKind(_symbolKind.Value) : _typeKind.HasValue ? new SymbolKindOrTypeKind(_typeKind.Value) : _methodKind.HasValue ? new SymbolKindOrTypeKind(_methodKind.Value) : - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } 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/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs index 87dec097f7a55..da6dd13f2a261 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs @@ -19,7 +19,7 @@ public SourceGeneratorItem(ProjectId projectId, ISourceGenerator generator, Anal : base(name: SourceGeneratorIdentity.GetGeneratorTypeName(generator)) { ProjectId = projectId; - Identity = new SourceGeneratorIdentity(generator); + Identity = new SourceGeneratorIdentity(generator, analyzerReference); AnalyzerReference = analyzerReference; } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/RuleSetDocumentExtensions.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/RuleSetDocumentExtensions.cs index 85a70c00d37c6..23c6f62401fe3 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/RuleSetDocumentExtensions.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/RuleSetDocumentExtensions.cs @@ -70,7 +70,7 @@ private static string ConvertReportDiagnosticToAction(ReportDiagnostic value) case ReportDiagnostic.Suppress: return "None"; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 6114e73abc7f4..510474d6f82af 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -136,38 +136,24 @@ await client.TryInvokeAsync( var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); - var callback = new DesignerAttributeComputerCallback(); + using var connection = client.CreateConnection(callbackTarget: null); - using var connection = client.CreateConnection(callback); - - var invokeTask = connection.TryInvokeAsync( + var stream = connection.TryInvokeStreamAsync( solution, - (service, checksum, callbackId, cancellationToken) => service.DiscoverDesignerAttributesAsync(callbackId, checksum, priorityDocument: null, cancellationToken), + (service, checksum, cancellationToken) => service.DiscoverDesignerAttributesAsync(checksum, solution.Projects.Single().Id, priorityDocument: null, cancellationToken), cancellationTokenSource.Token); - var infos = await callback.Infos; - Assert.Equal(1, infos.Length); + var items = new List(); + await foreach (var item in stream) + items.Add(item); + + Assert.Equal(1, items.Count); - var info = infos[0]; + var info = items[0]; Assert.Equal("Form", info.Category); Assert.Equal(solution.Projects.Single().Documents.Single().Id, info.DocumentId); cancellationTokenSource.Cancel(); - - Assert.True(await invokeTask); - } - - private class DesignerAttributeComputerCallback : IDesignerAttributeDiscoveryService.ICallback - { - private readonly TaskCompletionSource> _infosSource = new(); - - public Task> Infos => _infosSource.Task; - - public ValueTask ReportDesignerAttributeDataAsync(ImmutableArray infos, CancellationToken cancellationToken) - { - _infosSource.SetResult(infos); - return ValueTaskFactory.CompletedTask; - } } [Fact] 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/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 198f8de62aa33..41450c6228242 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -65,14 +65,18 @@ void Method() var ideOptions = new IdeAnalyzerOptions() { - CleanCodeGenerationOptions = new CleanCodeGenerationOptions( - CSharpCodeGenerationOptions.Default, - new CodeCleanupOptions( - FormattingOptions: CSharpSyntaxFormattingOptions.Default, - SimplifierOptions: new CSharpSimplifierOptions() + CleanCodeGenerationOptions = new() + { + GenerationOptions = CSharpCodeGenerationOptions.Default, + CleanupOptions = new() + { + FormattingOptions = CSharpSyntaxFormattingOptions.Default, + SimplifierOptions = new CSharpSimplifierOptions() { VarWhenTypeIsApparent = new CodeStyleOption2(false, NotificationOption2.Suggestion) - })) + } + } + } }; analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, ideOptions); diff --git a/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb b/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb index a0ccc30de99ed..3d0a9bddcf559 100644 --- a/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb +++ b/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb @@ -28,7 +28,6 @@ class C Public Async Function SnippetExpansionNoteAddedToDescription_ExactMatch() As Task Using state = CreateCSharpSnippetExpansionNoteTestState(_markup, "interface") - state.Workspace.GlobalOptions.SetGlobalOption(New OptionKey(CompletionOptionsStorage.ShowNewSnippetExperience, LanguageNames.CSharp), False) state.SendTypeChars("interfac") Await state.AssertCompletionSession() Await state.AssertSelectedCompletionItem(description:="title" & vbCrLf & @@ -137,6 +136,7 @@ class C Dim testSnippetInfoService = DirectCast(state.Workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(Of ISnippetInfoService)(), TestCSharpSnippetInfoService) testSnippetInfoService.SetSnippetShortcuts(snippetShortcuts) + state.Workspace.GlobalOptions.SetGlobalOption(New OptionKey(CompletionOptionsStorage.ShowNewSnippetExperience, LanguageNames.CSharp), False) Return state End Function End Class diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index c34059a43695f..7f89125a79027 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -134,6 +134,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics source.OnSolutionBuildCompleted() Await waiter.ExpeditedWaitAsync() + + Dim buildOnlyDiagnosticService = workspace.Services.GetRequiredService(Of IBuildOnlyDiagnosticsService) + Assert.Empty(buildOnlyDiagnosticService.GetBuildOnlyDiagnostics(project.DocumentIds.First())) + Assert.Empty(buildOnlyDiagnosticService.GetBuildOnlyDiagnostics(project.Id)) End Using End Using End Function @@ -258,6 +262,16 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Await waiter.ExpeditedWaitAsync() Assert.Equal(hasCompilationEndTag, buildDiagnosticCallbackSeen) + + Dim buildOnlyDiagnosticService = workspace.Services.GetRequiredService(Of IBuildOnlyDiagnosticsService) + Dim buildOnlyDiagnostics = buildOnlyDiagnosticService.GetBuildOnlyDiagnostics(project.Id) + If (hasCompilationEndTag) Then + Assert.Equal(1, buildOnlyDiagnostics.Length) + Assert.Equal(buildOnlyDiagnostics(0).Properties(WellKnownDiagnosticPropertyNames.Origin), WellKnownDiagnosticTags.Build) + Else + Assert.Empty(buildOnlyDiagnostics) + End If + End Using End Using End Function diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb index 6fb854c157fb1..289a3c0b5bd9f 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb @@ -25,16 +25,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Public Sub GetReferenceCalledMultipleTimes() - Dim composition = s_compositionWithMockDiagnosticUpdateSourceRegistrationService - Dim exportProvider = composition.ExportProviderFactory.CreateExportProvider() - - Using workspace = New TestWorkspace(composition:=composition) + Using workspace = New TestWorkspace(composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) Dim lazyWorkspace = New Lazy(Of VisualStudioWorkspace)( Function() Return Nothing End Function) - Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(exportProvider.GetExportedValue(Of IDiagnosticUpdateSourceRegistrationService)()) + Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(lazyWorkspace, registrationService) Using tempRoot = New TempRoot(), analyzer = New VisualStudioAnalyzer(tempRoot.CreateFile().Path, hostDiagnosticUpdateSource, ProjectId.CreateNewId(), LanguageNames.VisualBasic) @@ -48,20 +45,17 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Public Sub AnalyzerErrorsAreUpdated() - Dim composition = s_compositionWithMockDiagnosticUpdateSourceRegistrationService - Dim exportProvider = composition.ExportProviderFactory.CreateExportProvider() - - Dim lazyWorkspace = New Lazy(Of VisualStudioWorkspace)( - Function() - Return Nothing - End Function) + Using workspace = New TestWorkspace(composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) + Dim lazyWorkspace = New Lazy(Of VisualStudioWorkspace)( + Function() + Return Nothing + End Function) - Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(exportProvider.GetExportedValue(Of IDiagnosticUpdateSourceRegistrationService)()) - Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(lazyWorkspace, registrationService) + Dim file = Path.GetTempFileName() - Dim file = Path.GetTempFileName() + Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) + Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(lazyWorkspace, registrationService) - Using workspace = New TestWorkspace(composition:=composition) Dim globalOptions = workspace.GetService(Of IGlobalOptionService) Dim eventHandler = New EventHandlers(file, globalOptions) AddHandler hostDiagnosticUpdateSource.DiagnosticsUpdated, AddressOf eventHandler.DiagnosticAddedTest diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs index a2c2b77349327..4e10d076fc1d8 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs @@ -315,7 +315,7 @@ public int X1 VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); @@ -338,7 +338,7 @@ public int X1 VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); @@ -355,7 +355,7 @@ public int X1 VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpKeywordHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpKeywordHighlighting.cs index 70ee87a433272..4101c32a84c6a 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpKeywordHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpKeywordHighlighting.cs @@ -111,7 +111,7 @@ private void Verify(string marker, ImmutableArray expectedCount) VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.KeywordHighlighting); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs index 084910d2f7e39..0076e6120da71 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpReferenceHighlighting.cs @@ -119,7 +119,7 @@ private void Verify(string marker, IDictionary> VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.ReferenceHighlighting); @@ -143,7 +143,7 @@ private void VerifyNone(string marker) VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.ReferenceHighlighting); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSendToInteractive.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSendToInteractive.cs index 56311351efa04..8576f2a7493ed 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSendToInteractive.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpSendToInteractive.cs @@ -222,7 +222,7 @@ public void AddAssemblyReferenceAndTypesToInteractive() VisualStudio.InteractiveWindow.SubmitText("public class MyClass { public string MyFunc() { return \"MyClass.MyFunc()\"; } }"); VisualStudio.InteractiveWindow.SubmitText("(new MyClass()).MyFunc()"); VisualStudio.InteractiveWindow.WaitForLastReplOutput("\"MyClass.MyFunc()\""); - VisualStudio.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawler); + VisualStudio.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawlerLegacy); } [WpfFact] @@ -247,7 +247,7 @@ public void ResetInteractiveFromProjectAndVerify() VisualStudio.InteractiveWindow.SubmitText("System.Windows.Forms.Form f = new System.Windows.Forms.Form(); f.Text = \"goo\";"); VisualStudio.InteractiveWindow.SubmitText("f.Text"); VisualStudio.InteractiveWindow.WaitForLastReplOutput("\"goo\""); - VisualStudio.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawler); + VisualStudio.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawlerLegacy); } } } diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicKeywordHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicKeywordHighlighting.cs index 9c900c918e348..7c421690f84e9 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicKeywordHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicKeywordHighlighting.cs @@ -45,7 +45,7 @@ private void Verify(string marker, int expectedCount) VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.KeywordHighlighting); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs index 86ae41fe98a7f..ffaaab2f0234e 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicReferenceHighlighting.cs @@ -51,7 +51,7 @@ private void Verify(string marker, IDictionary> VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.ReferenceHighlighting); @@ -67,7 +67,7 @@ private void VerifyNone(string marker) VisualStudio.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification, FeatureAttribute.ReferenceHighlighting); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs index 56592b7cb5f91..b5a43beff355b 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs @@ -171,7 +171,7 @@ private async Task PasteAsync(string text, CancellationToken cancellationToken) var provider = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); var waiter = (IAsynchronousOperationWaiter)provider.GetListener(FeatureAttribute.AddImportsOnPaste); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler }, cancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy }, cancellationToken); Clipboard.SetText(text); await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd97CmdID.Paste, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpBuild.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpBuild.cs index 9134b15b0b21a..a3468dee04f89 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpBuild.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpBuild.cs @@ -42,7 +42,7 @@ static void Main(string[] args) await TestServices.Editor.SetTextAsync(editorText, HangMitigatingCancellationToken); - var buildSummary = await TestServices.SolutionExplorer.BuildSolutionAsync(waitForBuildToFinish: true, HangMitigatingCancellationToken); + var buildSummary = await TestServices.SolutionExplorer.BuildSolutionAndWaitAsync(HangMitigatingCancellationToken); Assert.Equal("========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========", buildSummary); await TestServices.ErrorList.ShowBuildErrorsAsync(HangMitigatingCancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index 51bdc857cef6b..e5acb4babfe26 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -224,7 +224,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -243,7 +243,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -270,7 +270,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -501,7 +501,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.EventHookup, FeatureAttribute.Rename, FeatureAttribute.RenameTracking, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -566,7 +566,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.EventHookup, FeatureAttribute.Rename, FeatureAttribute.RenameTracking, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -772,7 +772,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -809,7 +809,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -852,7 +852,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -872,7 +872,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -884,7 +884,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, @@ -979,7 +979,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -1145,7 +1145,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -1225,7 +1225,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -1359,7 +1359,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( new[] { FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, @@ -1399,7 +1399,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.EventHookup, FeatureAttribute.Rename, FeatureAttribute.RenameTracking, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, }, diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpErrorListCommon.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpErrorListCommon.cs index 6d267d1a55f69..b89a58e9cc049 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpErrorListCommon.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpErrorListCommon.cs @@ -43,7 +43,7 @@ static void Main(string[] args) "(Compiler) Class1.cs(4, 12): error CS0246: The type or namespace name 'P' could not be found (are you missing a using directive or an assembly reference?)", "(Compiler) Class1.cs(6, 24): error CS0117: 'Console' does not contain a definition for 'WriteLin'", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -52,9 +52,9 @@ static void Main(string[] args) var target = await TestServices.ErrorList.NavigateToErrorListItemAsync(0, isPreview: false, shouldActivate: true, HangMitigatingCancellationToken); Assert.Equal(expectedContents[0], target); Assert.Equal(25, await TestServices.Editor.GetCaretPositionAsync(HangMitigatingCancellationToken)); - await TestServices.SolutionExplorer.BuildSolutionAsync(waitForBuildToFinish: true, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.BuildSolutionAndWaitAsync(HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -77,7 +77,7 @@ static void Main(string[] args) var expectedContents = new[] { "(Compiler) Class1.cs(6, 13): warning CS0219: The variable 'unused' is assigned but its value is never used", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -99,7 +99,7 @@ static void Main(string[] args) ", HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); var expectedContents = new string[] { }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -108,14 +108,14 @@ static void Main(string[] args) await TestServices.Editor.ActivateAsync(HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("a = aa", charsOffset: -1, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("a", HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); expectedContents = new[] { "(Compiler) Class1.cs(7, 13): error CS0128: A local variable or function named 'aa' is already defined in this scope", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -126,7 +126,7 @@ static void Main(string[] args) await TestServices.Input.SendAsync(VirtualKeyCode.DELETE, HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); expectedContents = new string[] { }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -148,7 +148,7 @@ static void Main(string[] args) ", HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); var expectedContents = new string[] { }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -161,7 +161,7 @@ static void Main(string[] args) expectedContents = new[] { "(Compiler) Class1.cs(7, 13): error CS0128: A local variable or function named 'aa' is already defined in this scope", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -176,7 +176,7 @@ static void Main(string[] args) await TestServices.Input.SendAsync((VirtualKeyCode.F4, VirtualKeyCode.CONTROL), HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs index 44fd1e20ff7f3..d99c60b5f435d 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs @@ -76,8 +76,7 @@ void M() await WaitForNavigateAsync(HangMitigatingCancellationToken); // Assert we are in the right file now - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"Class1.cs{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"Class1.cs", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); Assert.Equal("Program", await TestServices.Editor.GetLineTextAfterCaretAsync(HangMitigatingCancellationToken)); } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs index b111ed82f8f84..5450cc1fe636f 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs @@ -44,8 +44,7 @@ await TestServices.Editor.SetTextAsync( }", HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("SomeClass", charsOffset: 0, HangMitigatingCancellationToken); await TestServices.Editor.GoToDefinitionAsync(HangMitigatingCancellationToken); - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileDef.cs{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileDef.cs", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.EditorVerifier.TextContainsAsync(@"class SomeClass$$", assertCaretPosition: true, HangMitigatingCancellationToken); Assert.False(await TestServices.Shell.IsActiveTabProvisionalAsync(HangMitigatingCancellationToken)); } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs index c7b3405f6ab82..f459519693391 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs @@ -69,8 +69,7 @@ await TestServices.Editor.SetTextAsync( identifierWithCaret = "$$Implementation"; } - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileImplementation.cs{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileImplementation.cs", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.EditorVerifier.TextContainsAsync($@"class {identifierWithCaret}", assertCaretPosition: true, HangMitigatingCancellationToken); Assert.False(await TestServices.Shell.IsActiveTabProvisionalAsync(HangMitigatingCancellationToken)); } @@ -174,8 +173,7 @@ public void SomeMethod() identifierWithCaret = "$$Implementation"; } - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileImplementation.cs{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileImplementation.cs", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.EditorVerifier.TextContainsAsync($@"class {identifierWithCaret} : IDisposable", assertCaretPosition: true, HangMitigatingCancellationToken); } @@ -223,8 +221,7 @@ void M() await TestServices.Editor.PlaceCaretAsync("Dispose", charsOffset: -1, HangMitigatingCancellationToken); // This one won't automatically navigate to the implementation - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileUsage.cs{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileUsage.cs", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.Editor.GoToImplementationAsync(HangMitigatingCancellationToken); Assert.Equal("'Dispose' implementations - Entire solution", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigateTo.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigateTo.cs index 4e57200fe8597..84414d1f3ed8e 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigateTo.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigateTo.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio; using Microsoft.VisualStudio.IntegrationTest.Utilities; using Roslyn.VisualStudio.IntegrationTests; +using Roslyn.VisualStudio.IntegrationTests.InProcess; using WindowsInput.Native; using Xunit; @@ -38,7 +39,7 @@ void FirstMethod() { } await TestServices.SolutionExplorer.AddFileAsync(project, "test2.cs", open: true, contents: @" ", cancellationToken: HangMitigatingCancellationToken); - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, HangMitigatingCancellationToken); + await TestServices.Shell.ShowNavigateToDialogAsync(HangMitigatingCancellationToken); await TestServices.Input.SendToNavigateToAsync("FirstMethod", VirtualKeyCode.RETURN); await TestServices.Workarounds.WaitForNavigationAsync(HangMitigatingCancellationToken); Assert.Equal($"test1.cs", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); @@ -49,12 +50,20 @@ await TestServices.SolutionExplorer.AddFileAsync(project, "test2.cs", open: true await TestServices.SolutionExplorer.AddProjectAsync(vbProject, WellKnownProjectTemplates.ClassLibrary, LanguageNames.VisualBasic, HangMitigatingCancellationToken); await TestServices.SolutionExplorer.AddFileAsync(vbProject, "vbfile.vb", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, HangMitigatingCancellationToken); + var isAllInOneSearch = await TestServices.Shell.ShowNavigateToDialogAsync(HangMitigatingCancellationToken); await TestServices.Input.SendToNavigateToAsync("FirstClass", VirtualKeyCode.RETURN); await TestServices.Workarounds.WaitForNavigationAsync(HangMitigatingCancellationToken); Assert.Equal($"test1.cs", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); Assert.Equal("FirstClass", await TestServices.Editor.GetSelectedTextAsync(HangMitigatingCancellationToken)); - await telemetry.VerifyFiredAsync(new[] { "vs/ide/vbcs/navigateto/search", "vs/platform/goto/launch" }, HangMitigatingCancellationToken); + + if (isAllInOneSearch) + { + await telemetry.VerifyFiredAsync(new[] { "vs/ide/vbcs/navigateto/search", "vs/ide/search/completed" }, HangMitigatingCancellationToken); + } + else + { + await telemetry.VerifyFiredAsync(new[] { "vs/ide/vbcs/navigateto/search", "vs/platform/goto/launch" }, HangMitigatingCancellationToken); + } } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNewDocumentFormatting.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNewDocumentFormatting.cs index 03bbb8fcb8114..54d0a22e80d50 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNewDocumentFormatting.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNewDocumentFormatting.cs @@ -138,7 +138,7 @@ public async Task CreateSDKProjectWithFileScopedNamespacesFromEditorConfig() private async Task VerifyNoErrorsAsync(CancellationToken cancellationToken) { await TestServices.ErrorList.ShowErrorListAsync(cancellationToken); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, cancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, cancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(cancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, Array.Empty()), diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpSourceGenerators.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpSourceGenerators.cs index 0cad7fca4b869..5637ff8b4f6d3 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpSourceGenerators.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpSourceGenerators.cs @@ -62,7 +62,7 @@ public static void Main() Assert.Equal(HelloWorldGenerator.GeneratedEnglishClassName, await TestServices.Editor.GetSelectedTextAsync(HangMitigatingCancellationToken)); } - [IdeTheory] + [IdeTheory(Skip = "https://github.com/dotnet/roslyn/issues/64721")] [CombinatorialData] public async Task FindReferencesForFileWithDefinitionInSourceGeneratedFile(bool invokeFromSourceGeneratedFile) { @@ -145,10 +145,10 @@ public static void Main() Assert.Equal(isPreview, await TestServices.Shell.IsActiveTabProvisionalAsync(HangMitigatingCancellationToken)); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/60477")] + [IdeFact] public async Task InvokeNavigateToForGeneratedFile() { - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, HangMitigatingCancellationToken); + await TestServices.Shell.ShowNavigateToDialogAsync(HangMitigatingCancellationToken); await TestServices.Input.SendToNavigateToAsync(HelloWorldGenerator.GeneratedEnglishClassName, VirtualKeyCode.RETURN); await TestServices.Workarounds.WaitForNavigationAsync(HangMitigatingCancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 6f965f3bf72b2..12fc07317af98 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -59,15 +59,6 @@ namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class EditorInProcess { - public async Task GetDirtyIndicatorAsync(CancellationToken cancellationToken) - { - var version = await TestServices.Shell.GetVersionAsync(cancellationToken); - if (version < Version.Parse("17.2.32224.407")) - return "*"; - - return " ⬤"; - } - public async Task WaitForEditorOperationsAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -516,7 +507,7 @@ public async Task SelectNavigationBarItemAsync(NavigationBarDropdownKind index, if (itemIndex < 0) { Assert.Contains(item, await GetNavigationBarItemsAsync(index, cancellationToken)); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } await ExpandNavigationBarAsync(index, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs index 189cead0b63e4..dfec4d5773b08 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorVerifierInProcess.cs @@ -238,7 +238,7 @@ public async Task CaretPositionAsync(int expectedCaretPosition, CancellationToke public async Task ErrorTagsAsync(string[] expectedTags, CancellationToken cancellationToken) { await TestServices.Workspace.WaitForAllAsyncOperationsAsync( - new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, + new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles }, cancellationToken); var actualTags = await TestServices.Editor.GetErrorTagsAsync(cancellationToken); @@ -250,7 +250,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( public async Task CurrentTokenTypeAsync(string tokenType, CancellationToken cancellationToken) { await TestServices.Workspace.WaitForAllAsyncOperationsAsync( - new[] { FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification }, + new[] { FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.Classification }, cancellationToken); var actualTokenTypes = await TestServices.Editor.GetCurrentClassificationsAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InputInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InputInProcess.cs index d2ed0238e9cf6..5194cb9ec0c56 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InputInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InputInProcess.cs @@ -81,19 +81,7 @@ internal async Task SendWithoutActivateAsync(Action callback, C }); } - internal Task SendToNavigateToAsync(params InputKey[] keys) - { - return SendToNavigateToAsync( - simulator => - { - foreach (var key in keys) - { - key.Apply(simulator); - } - }); - } - - internal async Task SendToNavigateToAsync(Action callback) + internal async Task SendToNavigateToAsync(params InputKey[] keys) { // AbstractSendKeys runs synchronously, so switch to a background thread before the call await TaskScheduler.Default; @@ -102,11 +90,22 @@ internal async Task SendToNavigateToAsync(Action callback) TestServices.JoinableTaskFactory.Run(async () => { await TestServices.JoinableTaskFactory.SwitchToMainThreadAsync(); - var searchBox = Assert.IsAssignableFrom(Keyboard.FocusedElement); - Assert.Equal("PART_SearchBox", searchBox.Name); + var searchBox = Assert.IsAssignableFrom(Keyboard.FocusedElement); + // Validate the focused control against the "old" search experience as well as the + // all-in-one search experience. + Assert.Contains(searchBox.Name, new[] { "PART_SearchBox", "SearchBoxControl" }); }); - callback(new InputSimulator()); + var inputSimulator = new InputSimulator(); + foreach (var key in keys) + { + key.Apply(inputSimulator); + + // Since the all-in-one search experience populates its results asychronously we need + // to give it time to update prior to applying the next InputKey otherwise we may apply + // a Return key meant to select an item before it is in the result set. + await Task.Delay(1000); + } TestServices.JoinableTaskFactory.Run(async () => { diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index df42602e0c778..968a355301d75 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -3,20 +3,70 @@ // See the LICENSE file in the project root for more information. using System; +using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Input; using Microsoft.CodeAnalysis.UnitTests; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Threading; +using Xunit; using IAsyncDisposable = System.IAsyncDisposable; namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class ShellInProcess { + /// True if the AllInOneSearch is being used for Navigation + public async Task ShowNavigateToDialogAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, cancellationToken); + + return await WaitForNavigateToFocusAsync(cancellationToken); + + async Task WaitForNavigateToFocusAsync(CancellationToken cancellationToken) + { + bool? isAllInOneSearchActive = null; + + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Take no direct action regarding activation, but assert the correct item already has focus + TestServices.JoinableTaskFactory.Run(async () => + { + await TestServices.JoinableTaskFactory.SwitchToMainThreadAsync(); + var searchBox = Assert.IsAssignableFrom(Keyboard.FocusedElement); + if ("PART_SearchBox" == searchBox.Name) + { + isAllInOneSearchActive = false; // Old search name + } + else if ("SearchBoxControl" == searchBox.Name) + { + isAllInOneSearchActive = true; // All-in-one search name + } + }); + + if (isAllInOneSearchActive.HasValue) + { + return isAllInOneSearchActive.Value; + } + + // If the dialog has not been displayed, then wait some time for it to show. The + // cancellation token passed in should be hang mitigating to avoid possible + // infinite loop. + await Task.Delay(100); + } + } + } + internal async Task IsActiveTabProvisionalAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -36,6 +86,19 @@ internal async Task IsActiveTabProvisionalAsync(CancellationToken cancella return (bool)isProvisionalObject; } + public async Task GetActiveDocumentFileNameAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var monitorSelection = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(monitorSelection.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_WindowFrame, out var windowFrameObj)); + var windowFrame = (IVsWindowFrame)windowFrameObj; + + ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_pszMkDocument, out var documentPathObj)); + var documentPath = (string)documentPathObj; + return Path.GetFileName(documentPath); + } + internal async Task GetMainWindowAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index 3731514283cd0..c84ffc4006c57 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -511,49 +511,35 @@ public async Task GetFileContentsAsync(string projectName, string relati } /// - /// If is , returns the build status line, which generally looks something like this: + /// The summary line for the build, which generally looks something like this: /// /// /// ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== /// - /// - /// Otherwise, this method does not wait for the build to complete and returns . /// - public async Task BuildSolutionAsync(bool waitForBuildToFinish, CancellationToken cancellationToken) + public async Task BuildSolutionAndWaitAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var buildOutputWindowPane = await GetBuildOutputWindowPaneAsync(cancellationToken); buildOutputWindowPane.Clear(); - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd97CmdID.BuildSln, cancellationToken); - if (waitForBuildToFinish) - { - return await WaitForBuildToFinishAsync(buildOutputWindowPane, cancellationToken); - } - - return null; - } - - /// - public async Task WaitForBuildToFinishAsync(CancellationToken cancellationToken) - { - var buildOutputWindowPane = await GetBuildOutputWindowPaneAsync(cancellationToken); - return await WaitForBuildToFinishAsync(buildOutputWindowPane, cancellationToken); - } + var buildManager = await GetRequiredGlobalServiceAsync(cancellationToken); + using var solutionEvents = new UpdateSolutionEvents(buildManager); + var buildCompleteTaskCompletionSource = new TaskCompletionSource(); - /// - /// The summary line for the build, which generally looks something like this: - /// - /// - /// ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========== - /// - /// - private async Task WaitForBuildToFinishAsync(IVsOutputWindowPane buildOutputWindowPane, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + void HandleUpdateSolutionDone() => buildCompleteTaskCompletionSource.SetResult(true); + solutionEvents.OnUpdateSolutionDone += HandleUpdateSolutionDone; + try + { + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd97CmdID.BuildSln, cancellationToken); - await KnownUIContexts.SolutionExistsAndNotBuildingAndNotDebuggingContext; + await buildCompleteTaskCompletionSource.Task; + } + finally + { + solutionEvents.OnUpdateSolutionDone -= HandleUpdateSolutionDone; + } // Force the error list to update ErrorHandler.ThrowOnFailure(buildOutputWindowPane.FlushToTaskList()); @@ -566,8 +552,17 @@ private async Task WaitForBuildToFinishAsync(IVsOutputWindowPane buildOu return string.Empty; } - // The build summary line should be second to last in the output window - return lines[^2].Extent.GetText(); + // Find the build summary line + for (var index = lines.Count - 1; index >= 0; index--) + { + var lineText = lines[index].Extent.GetText(); + if (lineText.StartsWith("========== Build:")) + { + return lineText; + } + } + + return string.Empty; } public async Task GetBuildOutputWindowPaneAsync(CancellationToken cancellationToken) @@ -682,4 +677,45 @@ private static string CreateTemporaryPath() }); } } + + internal sealed class UpdateSolutionEvents : IVsUpdateSolutionEvents, IDisposable + { + private uint _cookie; + private readonly IVsSolutionBuildManager2 _solutionBuildManager; + + public event Action? OnUpdateSolutionDone; + + internal UpdateSolutionEvents(IVsSolutionBuildManager2 solutionBuildManager) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + _solutionBuildManager = solutionBuildManager; + ErrorHandler.ThrowOnFailure(solutionBuildManager.AdviseUpdateSolutionEvents(this, out _cookie)); + } + + int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate) => VSConstants.E_NOTIMPL; + int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate) => VSConstants.E_NOTIMPL; + int IVsUpdateSolutionEvents.UpdateSolution_Cancel() => VSConstants.E_NOTIMPL; + int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => VSConstants.E_NOTIMPL; + + int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + { + OnUpdateSolutionDone?.Invoke(); + return 0; + } + + void IDisposable.Dispose() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + OnUpdateSolutionDone = null; + + if (_cookie != 0) + { + var tempCookie = _cookie; + _cookie = 0; + ErrorHandler.ThrowOnFailure(_solutionBuildManager.UnadviseUpdateSolutionEvents(tempCookie)); + } + } + } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkaroundsInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkaroundsInProcess.cs index 7ba83de6ac279..ebd9e90156414 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkaroundsInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkaroundsInProcess.cs @@ -32,7 +32,7 @@ public async Task WaitForLightBulbAsync(CancellationToken cancellationToken) // Wait for workspace (including project system, file change notifications, and EditorPackage operations), // as well as Roslyn's solution crawler and diagnostic service that report light bulb session changes. await TestServices.Workspace.WaitForAllAsyncOperationsAsync( - new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService }, + new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService }, cancellationToken); // Wait for operations dispatched to the main thread without other tracking diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkspaceInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkspaceInProcess.cs index 08e417feae448..d1183cf1d32d8 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkspaceInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/WorkspaceInProcess.cs @@ -34,7 +34,7 @@ internal partial class WorkspaceInProcess internal static void EnableAsynchronousOperationTracking() { - AsynchronousOperationListenerProvider.Enable(true); + AsynchronousOperationListenerProvider.Enable(true, diagnostics: true); } protected override async Task InitializeCoreAsync() diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicErrorListCommon.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicErrorListCommon.cs index 4c17f406a807a..e7fc8b5b1350c 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicErrorListCommon.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicErrorListCommon.cs @@ -43,7 +43,7 @@ End Module "(Compiler) Class1.vb(4, 24): error BC30002: Type 'P' is not defined.", "(Compiler) Class1.vb(9, 9): error BC30451: 'Goo' is not declared. It may be inaccessible due to its protection level.", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -51,9 +51,9 @@ End Module await TestServices.ErrorList.NavigateToErrorListItemAsync(0, isPreview: false, shouldActivate: true, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CaretPositionAsync(43, HangMitigatingCancellationToken); - await TestServices.SolutionExplorer.BuildSolutionAsync(waitForBuildToFinish: true, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.BuildSolutionAndWaitAsync(HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -77,7 +77,7 @@ End Namespace await TestServices.Input.SendAsync("F = 0", HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); var expectedContents = Array.Empty(); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); var actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -90,7 +90,7 @@ End Namespace expectedContents = new[] { "(Compiler) Class1.vb(6, 13): error BC30451: 'FF' is not declared. It may be inaccessible due to its protection level.", }; - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), @@ -101,7 +101,7 @@ End Namespace await TestServices.Input.SendAsync(VirtualKeyCode.DELETE, HangMitigatingCancellationToken); await TestServices.ErrorList.ShowErrorListAsync(HangMitigatingCancellationToken); expectedContents = Array.Empty(); - await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAllAsyncOperationsAsync(new[] { FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles, FeatureAttribute.ErrorList }, HangMitigatingCancellationToken); actualContents = await TestServices.ErrorList.GetErrorsAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff( string.Join(Environment.NewLine, expectedContents), diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs index f13cb9ed19b4b..6238425f6e69d 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs @@ -104,8 +104,7 @@ End Class await TestServices.FindReferencesWindow.NavigateToAsync(results[0], isPreview: false, shouldActivate: true, HangMitigatingCancellationToken); // Assert we are in the right file now - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"Class1.vb{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"Class1.vb", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); Assert.Equal("Alpha As Int32", await TestServices.Editor.GetLineTextAfterCaretAsync(HangMitigatingCancellationToken)); } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToDefinition.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToDefinition.cs index 47e2e1cfa08d1..ffe487f0de7d4 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToDefinition.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToDefinition.cs @@ -42,8 +42,7 @@ Dim gibberish As SomeClass End Class", HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("SomeClass", charsOffset: 0, HangMitigatingCancellationToken); await TestServices.Editor.GoToDefinitionAsync(HangMitigatingCancellationToken); - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileDef.vb{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileDef.vb", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.EditorVerifier.TextContainsAsync(@"Class SomeClass$$", assertCaretPosition: true, HangMitigatingCancellationToken); Assert.False(await TestServices.Shell.IsActiveTabProvisionalAsync(HangMitigatingCancellationToken)); } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToImplementation.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToImplementation.cs index 3aa4bf382b783..30d9263969aef 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToImplementation.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicGoToImplementation.cs @@ -66,8 +66,7 @@ await TestServices.Editor.SetTextAsync( identifierWithCaret = "$$Implementation"; } - var dirtyModifier = await TestServices.Editor.GetDirtyIndicatorAsync(HangMitigatingCancellationToken); - Assert.Equal($"FileImplementation.vb{dirtyModifier}", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + Assert.Equal($"FileImplementation.vb", await TestServices.Shell.GetActiveDocumentFileNameAsync(HangMitigatingCancellationToken)); await TestServices.EditorVerifier.TextContainsAsync($@"Class {identifierWithCaret}", assertCaretPosition: true); Assert.False(await TestServices.Shell.IsActiveTabProvisionalAsync(HangMitigatingCancellationToken)); } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigateTo.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigateTo.cs index 7b3c89a0ab05c..4bf131568256e 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigateTo.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigateTo.cs @@ -36,7 +36,7 @@ End Sub await TestServices.SolutionExplorer.AddFileAsync(project, "test2.vb", open: true, contents: @" ", cancellationToken: HangMitigatingCancellationToken); - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, HangMitigatingCancellationToken); + await TestServices.Shell.ShowNavigateToDialogAsync(HangMitigatingCancellationToken); await TestServices.Input.SendToNavigateToAsync("FirstMethod", VirtualKeyCode.RETURN); await TestServices.Workarounds.WaitForNavigationAsync(HangMitigatingCancellationToken); Assert.Equal($"test1.vb", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); @@ -46,7 +46,7 @@ await TestServices.SolutionExplorer.AddFileAsync(project, "test2.vb", open: true await TestServices.SolutionExplorer.AddProjectAsync(csProject, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); await TestServices.SolutionExplorer.AddFileAsync(csProject, "csfile.cs", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd12CmdID.NavigateTo, HangMitigatingCancellationToken); + await TestServices.Shell.ShowNavigateToDialogAsync(HangMitigatingCancellationToken); await TestServices.Input.SendToNavigateToAsync("FirstClass", VirtualKeyCode.RETURN); await TestServices.Workarounds.WaitForNavigationAsync(HangMitigatingCancellationToken); Assert.Equal($"test1.vb", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/Workspace/WorkspaceBase.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/Workspace/WorkspaceBase.cs index dcfcf658611a8..64ef416a8738a 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/Workspace/WorkspaceBase.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/Workspace/WorkspaceBase.cs @@ -102,7 +102,7 @@ public async Task ProjectReference() await TestServices.EditorVerifier.CurrentTokenTypeAsync("identifier", HangMitigatingCancellationToken); } - [IdeFact] + [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/64672")] public async Task ProjectProperties() { await TestServices.SolutionExplorer.CreateSolutionAsync(nameof(WorkspaceBase), HangMitigatingCancellationToken); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudioWorkspace_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudioWorkspace_InProc.cs index fe0865ff9ddd3..e28a45d8ac2b9 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudioWorkspace_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudioWorkspace_InProc.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Shared.Options; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Options; @@ -115,7 +116,9 @@ public void CleanUpWorkspace() => InvokeOnUIThread(cancellationToken => { LoadRoslynPackage(); - _visualStudioWorkspace.TestHookPartialSolutionsDisabled = true; + + var hook = _visualStudioWorkspace.Services.GetRequiredService(); + hook.IsPartialSolutionDisabled = true; }); /// diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs index 24ae3d2d14e7d..688501194ae8d 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs @@ -191,7 +191,7 @@ public void ErrorTags(params string[] expectedTags) _instance.Workspace.WaitForAllAsyncOperations( Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, - FeatureAttribute.SolutionCrawler, + FeatureAttribute.SolutionCrawlerLegacy, FeatureAttribute.DiagnosticService, FeatureAttribute.ErrorSquiggles); var actualTags = _textViewWindow.GetErrorTags(); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.cs index 25501e6599411..e8292a4c001f3 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.cs @@ -24,7 +24,7 @@ public ErrorList_OutOfProc(VisualStudioInstance visualStudioInstance) public void ShowErrorList() { - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawler); + _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawlerLegacy); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.DiagnosticService); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.ErrorSquiggles); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.ErrorList); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.Verifier.cs index a3c6a2cf7b4f2..e235ee3688dc5 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.Verifier.cs @@ -121,7 +121,7 @@ public void CodeActionsNotShowing() public void CurrentTokenType(string tokenType) { - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawler); + _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawlerLegacy); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.DiagnosticService); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Classification); var actualTokenTypes = _textViewWindow.GetCurrentClassifications(); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.cs index 186d9b71b2e5a..3d39b47071d0b 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/TextViewWindow_OutOfProc.cs @@ -94,7 +94,7 @@ public void InvokeCompletionList() public void InvokeCodeActionList() { - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawler); + _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.SolutionCrawlerLegacy); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.DiagnosticService); ShowLightBulb(); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownGlobalOptions.cs b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownGlobalOptions.cs index 90290712154fe..1ff64f52b6c2b 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownGlobalOptions.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/WellKnownGlobalOptions.cs @@ -56,7 +56,7 @@ public static IOption GetOption(this WellKnownGlobalOption option) WellKnownGlobalOption.SolutionCrawlerOptions_BackgroundAnalysisScopeOption => SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, WellKnownGlobalOption.SolutionCrawlerOptions_CompilerDiagnosticsScopeOption => SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, WellKnownGlobalOption.InlineRenameSessionOptions_UseNewUI => InlineRenameUIOptions.UseInlineAdornment, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; public static OptionKey GetKey(this WellKnownGlobalOption option, string? language) diff --git a/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/RemoteProjectInfoProvider.cs index 60b213a0c8066..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 { @@ -51,29 +52,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..d4d7258ff3ea1 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/Projects/RoslynRemoteProjectInfoProvider.cs @@ -12,7 +12,9 @@ using System.Threading; using System.Threading.Tasks; 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 @@ -75,35 +77,35 @@ 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(); + var 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 FileTextLoaderNoException(file, null)); - docInfos.Add(docInfo); - } + var docInfos = files.SelectAsArray(path => + DocumentInfo.Create( + DocumentId.CreateNewId(projectId), + name: Path.GetFileNameWithoutExtension(path), + loader: new WorkspaceFileTextLoaderNoException(services, path, defaultEncoding: null), + 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/FileTextLoaderNoException.cs b/src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs similarity index 60% rename from src/VisualStudio/LiveShare/Impl/Client/Projects/FileTextLoaderNoException.cs rename to src/VisualStudio/LiveShare/Impl/Client/Projects/WorkspaceFileTextLoaderNoException.cs index 5b6a7551133aa..13006ee243fd0 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) + internal 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(workspace, documentId, cancellationToken); + return base.LoadTextAndVersionAsync(options, cancellationToken); } } } diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 64c4184ab37b7..e24829a95679b 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; @@ -330,17 +331,28 @@ 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); } - 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(Services.SolutionServices, filePath, defaultEncoding: null), + filePath: filePath); + OnDocumentAdded(docInfo); - return CurrentSolution.GetDocument(docInfo.Id)!; + return CurrentSolution.GetRequiredDocument(docInfo.Id); } private static string? GetLanguage(string filePath) @@ -371,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 FileTextLoaderNoException(moniker, null)); + OnDocumentClosed(id, new WorkspaceFileTextLoaderNoException(Services.SolutionServices, moniker, defaultEncoding: null)); _openedDocs = _openedDocs.Remove(moniker); } } diff --git a/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs b/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs index 0556cf914f7e1..38d45f556eea8 100644 --- a/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs +++ b/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs @@ -15,6 +15,7 @@ using Microsoft.VisualStudio.LiveShare.LanguageServices; using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; +using Xunit.Abstractions; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.UnitTests { @@ -25,6 +26,10 @@ public abstract class AbstractLiveShareRequestHandlerTests : AbstractLanguageSer .AddParts(typeof(TestWorkspaceRegistrationService)) .AddParts(typeof(TestWorkspaceConfigurationService)); + protected AbstractLiveShareRequestHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + private class MockHostProtocolConverter : IHostProtocolConverter { private readonly Func _uriConversionFunction; diff --git a/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs b/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs index 4485bea00e9c3..043db4afeced7 100644 --- a/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs +++ b/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs @@ -9,15 +9,20 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.LanguageServer; using Xunit; +using Xunit.Abstractions; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.UnitTests { public class ProjectsHandlerTests : AbstractLiveShareRequestHandlerTests { + public ProjectsHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + { + } + [Fact] public async Task TestProjectsAsync() { - using var testLspServer = await CreateTestLspServerAsync(string.Empty); + await using var testLspServer = await CreateTestLspServerAsync(string.Empty); var solution = testLspServer.GetCurrentSolution(); var expected = solution.Projects.Select(p => CreateLspProject(p)).ToArray(); diff --git a/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj b/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj index d20787cc8e47b..c721f93e320f7 100644 --- a/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj +++ b/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj @@ -6,6 +6,7 @@ Roslyn.VisualStudio.Setup.Dependencies net472 true + false true diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index 9de4b3d229577..c7941dc80f9f3 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -6,6 +6,7 @@ Library Roslyn.VisualStudio.Setup net472 + false true @@ -132,6 +133,12 @@ true BindingRedirect + + Microsoft.CommonLanguageServerProtocolFramework + BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup + true + BindingRedirect + LiveShareLanguageServices BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup @@ -321,6 +328,7 @@ + diff --git a/src/VisualStudio/Setup/source.extension.vsixmanifest b/src/VisualStudio/Setup/source.extension.vsixmanifest index 76f80471a53d0..2b0025b170ea6 100644 --- a/src/VisualStudio/Setup/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup/source.extension.vsixmanifest @@ -68,6 +68,7 @@ + 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/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb index 6f3d771d61461..7542a106fb32b 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb @@ -18,7 +18,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly BC42024 As FixIdDefinition @@ -27,7 +27,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly SimplifyObjectCreationDiagnosticId As FixIdDefinition @@ -36,7 +36,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly UseIsNotExpressionDiagnosticId As FixIdDefinition End Class diff --git a/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj b/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj index cbaa107b07048..71a71b71da00f 100644 --- a/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj +++ b/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj @@ -7,6 +7,7 @@ true full + true false diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Roslyn.VisualStudio.DiagnosticsWindow.csproj b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Roslyn.VisualStudio.DiagnosticsWindow.csproj index df7379a87d59a..726c666a83112 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Roslyn.VisualStudio.DiagnosticsWindow.csproj +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Roslyn.VisualStudio.DiagnosticsWindow.csproj @@ -7,6 +7,7 @@ true Roslyn.VisualStudio.DiagnosticsWindow true + false true diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClient.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClient.cs index 391c100155d94..285ce8f856754 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClient.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClient.cs @@ -12,12 +12,17 @@ using Microsoft.CodeAnalysis.Editor.Xaml; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer; using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; +using Microsoft.VisualStudio.LanguageServices.Xaml.Telemetry; using Microsoft.VisualStudio.Utilities; +using StreamJsonRpc; namespace Microsoft.VisualStudio.LanguageServices.Xaml { @@ -29,16 +34,22 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml [Export(typeof(ILanguageClient))] internal class XamlInProcLanguageClient : AbstractInProcLanguageClient { + private readonly XamlProjectService _projectService; + private readonly IXamlLanguageServerFeedbackService? _feedbackService; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, true)] public XamlInProcLanguageClient( XamlLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLoggerFactory lspLoggerFactory, - IThreadingContext threadingContext) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext) + ILspServiceLoggerFactory lspLoggerFactory, + IThreadingContext threadingContext, + XamlProjectService projectService, + [Import(AllowDefault = true)] IXamlLanguageServerFeedbackService? feedbackService) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext) { + _projectService = projectService; + _feedbackService = feedbackService; } protected override ImmutableArray SupportedLanguages => ImmutableArray.Create(StringConstants.XamlLanguageName); @@ -50,6 +61,15 @@ public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapa return isLspExperimentEnabled ? XamlCapabilities.Current : XamlCapabilities.None; } + public override AbstractLanguageServer Create( + JsonRpc jsonRpc, + ICapabilitiesProvider capabilitiesProvider, + WellKnownLspServerKinds serverKind, + ILspServiceLogger logger) + { + return new XamlLanguageServer(LspServiceProvider, jsonRpc, capabilitiesProvider, logger, SupportedLanguages, serverKind, _projectService, _feedbackService); + } + /// /// Failures are only catastrophic when this server is providing intellisense features. /// diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClientDisableUX.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClientDisableUX.cs index 907d1ad7c97a2..fc7b6d158b0f8 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClientDisableUX.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageClient/XamlInProcLanguageClientDisableUX.cs @@ -37,10 +37,9 @@ internal class XamlInProcLanguageClientDisableUX : AbstractInProcLanguageClient public XamlInProcLanguageClientDisableUX( XamlLspServiceProvider lspServiceProvider, IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLoggerFactory lspLoggerFactory, + ILspServiceLoggerFactory lspLoggerFactory, IThreadingContext threadingContext) - : base(lspServiceProvider, globalOptions, listenerProvider, lspLoggerFactory, threadingContext) + : base(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext) { } diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs index 24d90641fcdc7..5115878bf4107 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs @@ -39,8 +39,8 @@ public CreateEventCommandHandler() public override bool RequiresLSPSolution => true; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(ExecuteCommandParams request) - => ((JToken)request.Arguments.First()).ToObject(); + public override TextDocumentIdentifier GetTextDocumentIdentifier(ExecuteCommandParams request) + => ((JToken)request.Arguments.First()).ToObject()!; public override async Task HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs index 7e5ed25a9cd94..58fb1d5b8fec4 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs @@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler /// [ExportStatelessXamlLspService(typeof(CompletionHandler)), Shared] [Method(Methods.TextDocumentCompletionName)] - internal class CompletionHandler : IRequestHandler + internal class CompletionHandler : ILspServiceDocumentRequestHandler { private const string CreateEventHandlerCommandTitle = "Create Event Handler"; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs index f0b39ce749a7a..15b83489ef506 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs @@ -29,7 +29,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler /// [ExportStatelessXamlLspService(typeof(CompletionResolveHandler)), Shared] [Method(LSP.Methods.TextDocumentCompletionResolveName)] - internal class CompletionResolveHandler : IRequestHandler + internal class CompletionResolveHandler : ILspServiceRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -43,8 +43,6 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public TextDocumentIdentifier? GetTextDocumentIdentifier(CompletionItem request) => null; - public async Task HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Solution); diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs index 347792cb85aae..996ffafa02822 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs @@ -29,7 +29,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageSe { [ExportStatelessXamlLspService(typeof(GoToDefinitionHandler)), Shared] [Method(Methods.TextDocumentDefinitionName)] - internal class GoToDefinitionHandler : IRequestHandler + internal class GoToDefinitionHandler : ILspServiceRequestHandler { private readonly IMetadataAsSourceFileService _metadataAsSourceFileService; private readonly IGlobalOptionService _globalOptions; @@ -46,7 +46,7 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 9fad5083ed7da..d3675f39922cb 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageSe /// /// Root type for both document and workspace diagnostic pull requests. /// - internal abstract class AbstractPullDiagnosticHandler : IRequestHandler + internal abstract class AbstractPullDiagnosticHandler : ILspServiceRequestHandler where TReport : VSInternalDiagnosticReport { private readonly IXamlPullDiagnosticService _xamlDiagnosticService; @@ -189,7 +189,5 @@ private static DiagnosticTag[] ConvertTags(XamlDiagnostic diagnostic) return result.ToArray(); } - - public abstract TextDocumentIdentifier? GetTextDocumentIdentifier(TDiagnosticsParams request); } } diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index cc0445d4fd897..ff110d15f05e4 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Editor.Xaml; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; @@ -17,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageSe { [ExportStatelessXamlLspService(typeof(DocumentPullDiagnosticHandler)), Shared] [Method(VSInternalMethods.DocumentPullDiagnosticName)] - internal class DocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler + internal class DocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler, ITextDocumentIdentifierHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -26,7 +27,7 @@ public DocumentPullDiagnosticHandler( : base(xamlPullDiagnosticService) { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalDocumentDiagnosticsParams request) + public TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalDocumentDiagnosticsParams request) => request.TextDocument; protected override VSInternalDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index 77ae57185664e..65da76a99fc8c 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -30,8 +30,6 @@ public WorkspacePullDiagnosticHandler( : base(xamlPullDiagnosticService) { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceDiagnosticsParams request) => null; - protected override VSInternalWorkspaceDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) => new VSInternalWorkspaceDiagnosticReport { TextDocument = identifier, Diagnostics = diagnostics, ResultId = resultId }; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs index c6abb7778fce9..1e2940964bc3d 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportStatelessXamlLspService(typeof(FoldingRangesHandler)), Shared] [Method(Methods.TextDocumentFoldingRangeName)] - internal class FoldingRangesHandler : IRequestHandler + internal class FoldingRangesHandler : ILspServiceRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -29,7 +29,7 @@ public FoldingRangesHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(FoldingRangeParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(FoldingRangeParams request) => request.TextDocument; public async Task HandleRequestAsync(FoldingRangeParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index cbb2c25e72aa3..1b90c057e9d39 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -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. +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -15,12 +16,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - internal abstract class AbstractFormatDocumentHandlerBase : IRequestHandler + internal abstract class AbstractFormatDocumentHandlerBase : ILspServiceRequestHandler { public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public abstract TextDocumentIdentifier? GetTextDocumentIdentifier(RequestType request); + public abstract TextDocumentIdentifier GetTextDocumentIdentifier(RequestType request); public abstract Task HandleRequestAsync(RequestType request, RequestContext context, CancellationToken cancellationToken); protected async Task GetTextEditsAsync(LSP.FormattingOptions formattingOptions, RequestContext context, CancellationToken cancellationToken, LSP.Range? range = null) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs index 1f0dbf4b6e037..4e7738babafad 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs @@ -23,7 +23,7 @@ public FormatDocumentHandler() { } - public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; + public override LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync(LSP.DocumentFormattingParams request, RequestContext context, CancellationToken cancellationToken) => GetTextEditsAsync(request.Options, context, cancellationToken); diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs index c6142e11fc63a..ea3fc5a059972 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -21,7 +21,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportStatelessXamlLspService(typeof(FormatDocumentOnTypeHandler)), Shared] [Method(Methods.TextDocumentOnTypeFormattingName)] - internal class FormatDocumentOnTypeHandler : IRequestHandler + internal class FormatDocumentOnTypeHandler : ILspServiceRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -32,7 +32,7 @@ public FormatDocumentOnTypeHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentOnTypeFormattingParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentOnTypeFormattingParams request) => request.TextDocument; public async Task HandleRequestAsync(DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs index 618b5863e4b45..758db2c77e47c 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -23,7 +23,7 @@ public FormatDocumentRangeHandler() { } - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; + public override TextDocumentIdentifier GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync(DocumentRangeFormattingParams request, RequestContext context, CancellationToken cancellationToken) => GetTextEditsAsync(request.Options, context, cancellationToken, range: request.Range); diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs index 4abb4fe30eb42..c8fe68122a192 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs @@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportStatelessXamlLspService(typeof(HoverHandler)), Shared] [Method(Methods.TextDocumentHoverName)] - internal sealed class HoverHandler : IRequestHandler + internal sealed class HoverHandler : ILspServiceRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -39,7 +39,7 @@ public HoverHandler(IGlobalOptionService globalOptions) public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentPositionParams request) => request.TextDocument; public async Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 6addf10e35bb9..48ff038c8ddaf 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportStatelessXamlLspService(typeof(OnAutoInsertHandler)), Shared] [Method(VSInternalMethods.OnAutoInsertName)] - internal class OnAutoInsertHandler : IRequestHandler + internal class OnAutoInsertHandler : ILspServiceRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -29,7 +29,7 @@ public OnAutoInsertHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; public async Task HandleRequestAsync(VSInternalDocumentOnAutoInsertParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs index 51aedefa845b2..b4f92bb08d67c 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs @@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportStatelessXamlLspService(typeof(OnTypeRenameHandler)), Shared] [Method(Methods.TextDocumentLinkedEditingRangeName)] - internal class OnTypeRenameHandler : IRequestHandler + internal class OnTypeRenameHandler : ILspServiceRequestHandler { // From https://www.w3.org/TR/xml/#NT-NameStartChar // Notes: @@ -61,7 +61,7 @@ public OnTypeRenameHandler() public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier? GetTextDocumentIdentifier(LinkedEditingRangeParams request) => request.TextDocument; + public TextDocumentIdentifier GetTextDocumentIdentifier(LinkedEditingRangeParams request) => request.TextDocument; public async Task HandleRequestAsync(LinkedEditingRangeParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLanguageServer.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLanguageServer.cs new file mode 100644 index 0000000000000..74ada75889b89 --- /dev/null +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLanguageServer.cs @@ -0,0 +1,44 @@ +// 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.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; +using Microsoft.VisualStudio.LanguageServices.Xaml.Telemetry; +using StreamJsonRpc; + +namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer; +internal class XamlLanguageServer : RoslynLanguageServer +{ + private readonly XamlProjectService _projectService; + private readonly IXamlLanguageServerFeedbackService? _feedbackService; + + public XamlLanguageServer( + AbstractLspServiceProvider lspServiceProvider, + JsonRpc jsonRpc, + ICapabilitiesProvider capabilitiesProvider, + ILspServiceLogger logger, + ImmutableArray supportedLanguages, + WellKnownLspServerKinds serverKind, + XamlProjectService projectService, + IXamlLanguageServerFeedbackService? feedbackService) : base(lspServiceProvider, jsonRpc, capabilitiesProvider, logger, supportedLanguages, serverKind) + { + _projectService = projectService; + _feedbackService = feedbackService; + } + + protected override IRequestExecutionQueue ConstructRequestExecutionQueue() + { + var queue = new XamlRequestExecutionQueue(_projectService, _feedbackService, this, _logger, GetHandlerProvider()); + queue.Start(); + return queue; + } +} diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLspServiceProvider.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLspServiceProvider.cs index e86cf0a4b6c37..1bc78220bf9ad 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLspServiceProvider.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlLspServiceProvider.cs @@ -18,7 +18,8 @@ internal class XamlLspServiceProvider : AbstractLspServiceProvider [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public XamlLspServiceProvider( [ImportMany(StringConstants.XamlLspLanguagesContract)] IEnumerable> lspServices, - [ImportMany(StringConstants.XamlLspLanguagesContract)] IEnumerable> lspServiceFactories) : base(lspServices, lspServiceFactories) + [ImportMany(StringConstants.XamlLspLanguagesContract)] IEnumerable> lspServiceFactories) + : base(lspServices, lspServiceFactories) { } } diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs deleted file mode 100644 index dd3d3264b6881..0000000000000 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs +++ /dev/null @@ -1,89 +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.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Xaml; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Microsoft.VisualStudio.LanguageServices.Xaml.Telemetry; - -namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer -{ - /// - /// Implements the Language Server Protocol for XAML - /// - [ExportLspServiceFactory(typeof(RequestDispatcher), StringConstants.XamlLspLanguagesContract), Shared] - internal sealed class XamlRequestDispatcherFactory : RequestDispatcherFactory - { - private readonly XamlProjectService _projectService; - private readonly IXamlLanguageServerFeedbackService? _feedbackService; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public XamlRequestDispatcherFactory( - XamlProjectService projectService, - [Import(AllowDefault = true)] IXamlLanguageServerFeedbackService? feedbackService) - { - _projectService = projectService; - _feedbackService = feedbackService; - } - - public override ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - return new XamlRequestDispatcher(_projectService, lspServices, _feedbackService); - } - - private class XamlRequestDispatcher : RequestDispatcher - { - private readonly XamlProjectService _projectService; - private readonly IXamlLanguageServerFeedbackService? _feedbackService; - - public XamlRequestDispatcher( - XamlProjectService projectService, - LspServices services, - IXamlLanguageServerFeedbackService? feedbackService) : base(services) - { - _projectService = projectService; - _feedbackService = feedbackService; - } - - protected override async Task ExecuteRequestAsync( - RequestExecutionQueue queue, bool mutatesSolutionState, bool requiresLSPSolution, IRequestHandler handler, TRequestType request, ClientCapabilities clientCapabilities, string methodName, CancellationToken cancellationToken) - where TRequestType : class - where TResponseType : default - { - var textDocument = handler.GetTextDocumentIdentifier(request); - - DocumentId? documentId = null; - if (textDocument is { Uri: { IsAbsoluteUri: true } documentUri }) - { - documentId = _projectService.TrackOpenDocument(documentUri.LocalPath); - } - - using (var requestScope = _feedbackService?.CreateRequestScope(documentId, methodName)) - { - try - { - return await base.ExecuteRequestAsync(queue, mutatesSolutionState, requiresLSPSolution, handler, request, clientCapabilities, methodName, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (e is not OperationCanceledException) - { - // Inform Xaml language service that the RequestScope failed. - // This doesn't send the exception to Telemetry or Watson - requestScope?.RecordFailure(e); - throw; - } - } - } - } - } -} diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs new file mode 100644 index 0000000000000..e040d8f6aca8c --- /dev/null +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs @@ -0,0 +1,77 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Xaml; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.VisualStudio.LanguageServices.Xaml.Telemetry; + +namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer +{ + internal class XamlRequestExecutionQueue : RequestExecutionQueue, ILspService + { + private readonly XamlProjectService _projectService; + private readonly IXamlLanguageServerFeedbackService? _feedbackService; + + public XamlRequestExecutionQueue( + XamlProjectService projectService, + IXamlLanguageServerFeedbackService? feedbackService, + AbstractLanguageServer languageServer, + ILspLogger logger, + IHandlerProvider handlerProvider) : base(languageServer, logger, handlerProvider) + { + _projectService = projectService; + _feedbackService = feedbackService; + } + + public override async Task ExecuteAsync( + TRequestType request, + string methodName, + ILspServices lspServices, + CancellationToken cancellationToken) + { + var methodHandler = GetMethodHandler(methodName); + TextDocumentIdentifier? textDocument = null; + if (methodHandler is ITextDocumentIdentifierHandler txtDocumentIdentifierHandler) + { + if (txtDocumentIdentifierHandler is ITextDocumentIdentifierHandler t) + { + textDocument = t.GetTextDocumentIdentifier(request); + } + } + + DocumentId? documentId = null; + if (textDocument is { Uri: { IsAbsoluteUri: true } documentUri }) + { + documentId = _projectService.TrackOpenDocument(documentUri.LocalPath); + } + + using (var requestScope = _feedbackService?.CreateRequestScope(documentId, methodName)) + { + try + { + return await base.ExecuteAsync( + request, methodName, lspServices, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (e is not OperationCanceledException) + { + // Inform Xaml language service that the RequestScope failed. + // This doesn't send the exception to Telemetry or Watson + requestScope?.RecordFailure(e); + throw; + } + } + } + } +} diff --git a/src/VisualStudio/Xaml/Impl/Microsoft.VisualStudio.LanguageServices.Xaml.csproj b/src/VisualStudio/Xaml/Impl/Microsoft.VisualStudio.LanguageServices.Xaml.csproj index 0c80cc28c8b31..4c8f5a2006c23 100644 --- a/src/VisualStudio/Xaml/Impl/Microsoft.VisualStudio.LanguageServices.Xaml.csproj +++ b/src/VisualStudio/Xaml/Impl/Microsoft.VisualStudio.LanguageServices.Xaml.csproj @@ -7,6 +7,7 @@ false net472 None + true diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/AttributeGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/AttributeGenerator.cs index 1186e6a707be2..54176208bb43b 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/AttributeGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/AttributeGenerator.cs @@ -81,30 +81,6 @@ public static SyntaxList GenerateAttributeLists( : null; } - private static bool IsCompilerInternalAttribute(AttributeData attribute) - { - // from https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md - var attrClass = attribute.AttributeClass; - if (attrClass == null) - return false; - - var name = attrClass.Name; - - if (name is not "NullableAttribute" and - not "NullableContextAttribute" and - not "NativeIntegerAttribute" and - not "DynamicAttribute") - { - return false; - } - - var ns = attrClass.ContainingNamespace; - return ns?.Name == nameof(System.Runtime.CompilerServices) && - ns.ContainingNamespace?.Name == nameof(System.Runtime) && - ns.ContainingNamespace.ContainingNamespace?.Name == nameof(System) && - ns.ContainingNamespace.ContainingNamespace.ContainingNamespace?.IsGlobalNamespace == true; - } - private static AttributeArgumentListSyntax? GenerateAttributeArgumentList(AttributeData attribute) { if (attribute.ConstructorArguments.Length == 0 && attribute.NamedArguments.Length == 0) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/ExpressionGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/ExpressionGenerator.cs index 560b485af8bac..dfadfd32b0e5b 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/ExpressionGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/ExpressionGenerator.cs @@ -266,7 +266,7 @@ private static ExpressionSyntax GenerateLiteralExpression( var suffix = DetermineSuffix(type, nonNegativeValue); var stringValue = negative && nonNegativeValue.Equals(value) - ? (integerMinValueString ?? throw ExceptionUtilities.Unreachable) + ? (integerMinValueString ?? throw ExceptionUtilities.Unreachable()) : ((IFormattable)nonNegativeValue).ToString(formatString, CultureInfo.InvariantCulture) + suffix; var literal = SyntaxFactory.LiteralExpression( diff --git a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs index e1b310422faee..bf89d661d7bb3 100644 --- a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs @@ -190,6 +190,7 @@ protected override void AddLocalFunctionInfos( containerDisplayName, fullyQualifiedContainerName, isPartial: false, + hasAttributes: localFunction.AttributeLists.Any(), DeclaredSymbolInfoKind.Method, Accessibility.Private, localFunction.Identifier.Span, @@ -225,6 +226,7 @@ protected override void AddLocalFunctionInfos( containerDisplayName, fullyQualifiedContainerName, typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword), + typeDeclaration.AttributeLists.Any(), typeDeclaration.Kind() switch { SyntaxKind.ClassDeclaration => DeclaredSymbolInfoKind.Class, @@ -237,7 +239,8 @@ protected override void AddLocalFunctionInfos( GetAccessibility(container, typeDeclaration.Modifiers), typeDeclaration.Identifier.Span, GetInheritanceNames(stringTable, typeDeclaration.BaseList), - IsNestedType(typeDeclaration)); + IsNestedType(typeDeclaration), + typeParameterCount: typeDeclaration.TypeParameterList?.Parameters.Count ?? 0); } protected override DeclaredSymbolInfo GetEnumDeclarationInfo( @@ -253,6 +256,7 @@ protected override DeclaredSymbolInfo GetEnumDeclarationInfo( containerDisplayName, fullyQualifiedContainerName, enumDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword), + enumDeclaration.AttributeLists.Any(), DeclaredSymbolInfoKind.Enum, GetAccessibility(container, enumDeclaration.Modifiers), enumDeclaration.Identifier.Span, @@ -280,6 +284,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, ctorDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + ctorDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Constructor, GetAccessibility(container, ctorDecl.Modifiers), ctorDecl.Identifier.Span, @@ -295,6 +300,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, delegateDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + delegateDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Delegate, GetAccessibility(container, delegateDecl.Modifiers), delegateDecl.Identifier.Span, @@ -308,6 +314,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, enumMember.Modifiers.Any(SyntaxKind.PartialKeyword), + enumMember.AttributeLists.Any(), DeclaredSymbolInfoKind.EnumMember, Accessibility.Public, enumMember.Identifier.Span, @@ -321,6 +328,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, eventDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + eventDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Event, GetAccessibility(container, eventDecl.Modifiers), eventDecl.Identifier.Span, @@ -334,6 +342,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, indexerDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + indexerDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Indexer, GetAccessibility(container, indexerDecl.Modifiers), indexerDecl.ThisKeyword.Span, @@ -348,6 +357,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, method.Modifiers.Any(SyntaxKind.PartialKeyword), + method.AttributeLists.Any(), isExtensionMethod ? DeclaredSymbolInfoKind.ExtensionMethod : DeclaredSymbolInfoKind.Method, GetAccessibility(container, method.Modifiers), method.Identifier.Span, @@ -363,6 +373,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, property.Modifiers.Any(SyntaxKind.PartialKeyword), + property.AttributeLists.Any(), DeclaredSymbolInfoKind.Property, GetAccessibility(container, property.Modifiers), property.Identifier.Span, @@ -385,6 +396,7 @@ protected override void AddMemberDeclarationInfos( containerDisplayName, fullyQualifiedContainerName, fieldDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword), + fieldDeclaration.AttributeLists.Any(), kind, GetAccessibility(container, fieldDeclaration.Modifiers), variableDeclarator.Identifier.Span, @@ -434,6 +446,7 @@ protected override void AddSynthesizedDeclaredSymbolInfos( containerDisplayName, fullyQualifiedContainerName, isPartial: false, + parameter.AttributeLists.Any(), DeclaredSymbolInfoKind.Property, Accessibility.Public, parameter.Identifier.Span, diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 99a9b20d18709..84504eb8df955 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -430,7 +430,7 @@ private async Task RenameAndAnnotateAsync(SyntaxToken token, Syntax } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -931,7 +931,7 @@ renamedSymbol.ContainingSymbol is IMethodSymbol methodSymbol && } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -959,7 +959,7 @@ renamedSymbol.ContainingSymbol is IMethodSymbol methodSymbol && } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs index 95239f4810334..9b2ffeaefbc75 100644 --- a/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs @@ -19,7 +19,6 @@ namespace Microsoft.CodeAnalysis.CSharp.SemanticModelReuse [ExportLanguageService(typeof(ISemanticModelReuseLanguageService), LanguageNames.CSharp), Shared] internal class CSharpSemanticModelReuseLanguageService : AbstractSemanticModelReuseLanguageService< MemberDeclarationSyntax, - BaseMethodDeclarationSyntax, BasePropertyDeclarationSyntax, AccessorDeclarationSyntax> { @@ -56,14 +55,8 @@ protected override SyntaxList GetAccessors(BaseProper return null; } - protected override async Task TryGetSpeculativeSemanticModelWorkerAsync( - SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken) + protected override SemanticModel? TryGetSpeculativeSemanticModelWorker(SemanticModel previousSemanticModel, SyntaxNode previousBodyNode, SyntaxNode currentBodyNode) { - var previousRoot = await previousSemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - var currentRoot = await currentBodyNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - - var previousBodyNode = GetPreviousBodyNode(previousRoot, currentRoot, currentBodyNode); - if (previousBodyNode is BaseMethodDeclarationSyntax previousBaseMethod && currentBodyNode is BaseMethodDeclarationSyntax currentBaseMethod && previousBaseMethod.Body != null && 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..fb4a502503426 --- /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((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); + + 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..4d7fa069eabc7 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs @@ -58,7 +58,8 @@ internal static SyntaxTree CreateRecoverableTree( ProjectId cacheKey, string filePath, ParseOptions options, - ValueSource text, + 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 5cd6bf1db00d4..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; @@ -69,10 +70,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,17 +92,18 @@ public override SyntaxTree CreateRecoverableTree( ProjectId cacheKey, string filePath, ParseOptions options, - ValueSource text, - Encoding encoding, - SyntaxNode root) + ITextAndVersionSource text, + 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..fc0b38c7f3b1e 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SymbolEditorTests.cs @@ -37,7 +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), VersionStamp.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/CSharpTest/Formatting/FormattingTests_Patterns.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs index cf72f24103d49..aa67130b0cf0e 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs @@ -66,7 +66,7 @@ bool Method(int value) BinaryOperatorSpacingOptions.Single => expectedSingle, BinaryOperatorSpacingOptions.Ignore => expectedIgnore, BinaryOperatorSpacingOptions.Remove => expectedRemove, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var changingOptions = new OptionsCollection(LanguageNames.CSharp) @@ -156,7 +156,7 @@ bool Method(int value) (BinaryOperatorSpacingOptions.Single, true) => expectedSingleTrue, (BinaryOperatorSpacingOptions.Ignore, true) => expectedIgnoreTrue, (BinaryOperatorSpacingOptions.Remove, true) => expectedRemoveTrue, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var changingOptions = new OptionsCollection(LanguageNames.CSharp) @@ -214,7 +214,7 @@ bool Method(int value) BinaryOperatorSpacingOptions.Single => expectedSingle, BinaryOperatorSpacingOptions.Ignore => expectedIgnore, BinaryOperatorSpacingOptions.Remove => expectedRemove, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var changingOptions = new OptionsCollection(LanguageNames.CSharp) @@ -303,7 +303,7 @@ bool Method(int value) (BinaryOperatorSpacingOptions.Single, true) => expectedSingleTrue, (BinaryOperatorSpacingOptions.Ignore, true) => expectedIgnoreTrue, (BinaryOperatorSpacingOptions.Remove, true) => expectedRemoveTrue, - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; var changingOptions = new OptionsCollection(LanguageNames.CSharp) 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 e61eec56067ab..a83f54039c58e 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 () => @@ -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 static ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) + private ImmutableArray CreateDocumentInfos(IReadOnlyList documentFileInfos, ProjectId projectId, Encoding? encoding) { var results = ImmutableArray.CreateBuilder(); @@ -454,7 +452,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/MSBuild/MSBuild/ProjectFile/ProjectFile.cs b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFile.cs index ba79356f1c360..62c2ac0ad3ad4 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFile.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFile.cs @@ -127,6 +127,12 @@ private ProjectFileInfo CreateProjectFileInfo(MSB.Execution.ProjectInstance proj outputRefFilePath = GetAbsolutePathRelativeToProject(outputRefFilePath); } + var intermediateOutputFilePath = project.GetItems(ItemNames.IntermediateAssembly).FirstOrDefault()?.EvaluatedInclude; + if (!RoslynString.IsNullOrWhiteSpace(intermediateOutputFilePath)) + { + intermediateOutputFilePath = GetAbsolutePathRelativeToProject(intermediateOutputFilePath); + } + // Right now VB doesn't have the concept of "default namespace". But we conjure one in workspace // by assigning the value of the project's root namespace to it. So various feature can choose to // use it for their own purpose. @@ -158,6 +164,7 @@ private ProjectFileInfo CreateProjectFileInfo(MSB.Execution.ProjectInstance proj project.FullPath, outputFilePath, outputRefFilePath, + intermediateOutputFilePath, defaultNamespace, targetFramework, commandLineArgs, diff --git a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileInfo.cs b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileInfo.cs index 6a8a54b8fe05b..561322651a523 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileInfo.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/ProjectFile/ProjectFileInfo.cs @@ -28,6 +28,11 @@ internal sealed class ProjectFileInfo /// 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/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs index 00b7092781bf6..ef3f1dd15f895 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs @@ -44,7 +44,7 @@ public async Task AddSemanticClassificationsAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs index a9e3f5ad2a256..acbbc845bfdc2 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/SyntacticChangeRangeComputer.cs @@ -239,7 +239,7 @@ int ComputeCommonRightWidth() // We consumed both stacks entirely. That means the trees were identical (though the root was // different). We should never get here. If we were the same, then walking from the left should have // consumed everything and already bailed out. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs index 94a087fe97b9e..7c30b95ceb3df 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs @@ -156,7 +156,7 @@ public CodeRefactoringFixAllProviderInfo( } public override bool CanBeFixed(Diagnostic diagnostic) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/CodeGeneration/AbstractCodeGenerationService.cs b/src/Workspaces/Core/Portable/CodeGeneration/AbstractCodeGenerationService.cs index a7a348b3634b5..4eba9dcc7b8dc 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/AbstractCodeGenerationService.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/AbstractCodeGenerationService.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; diff --git a/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationHelpers.cs b/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationHelpers.cs index 5680c24b4a84b..0a15426b4e6f4 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationHelpers.cs @@ -396,5 +396,25 @@ public static int GetInsertionIndex( // Couldn't find anything with our name. Just place us at the end of this group. return desiredGroupIndex; } + + // from https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md + public static bool IsCompilerInternalAttribute(AttributeData attribute) + => attribute.AttributeClass is + { + Name: "NullableAttribute" or "NullableContextAttribute" or "NativeIntegerAttribute" or "DynamicAttribute", + ContainingNamespace: + { + Name: nameof(System.Runtime.CompilerServices), + ContainingNamespace: + { + Name: nameof(System.Runtime), + ContainingNamespace: + { + Name: nameof(System), + ContainingNamespace.IsGlobalNamespace: true, + }, + }, + }, + }; } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs index 80782f12f47ad..8d425ae0f4aa5 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; @@ -36,6 +38,12 @@ internal sealed partial class DiagnosticAnalyzerInfoCache /// private readonly ConditionalWeakTable _descriptorsInfo; + /// + /// Lazily populated map from diagnostic IDs to diagnostic descriptor. + /// If same diagnostic ID is reported by multiple descriptors, a null value is stored in the map for that ID. + /// + private readonly ConcurrentDictionary _idToDescriptorsMap; + private sealed class DiagnosticDescriptorsInfo { public readonly ImmutableArray SupportedDescriptors; @@ -65,6 +73,7 @@ public SharedGlobalCache() internal DiagnosticAnalyzerInfoCache() { _descriptorsInfo = new ConditionalWeakTable(); + _idToDescriptorsMap = new ConcurrentDictionary(); } /// @@ -98,10 +107,13 @@ public bool IsCompilationEndAnalyzer(DiagnosticAnalyzer analyzer) public bool IsTelemetryCollectionAllowed(DiagnosticAnalyzer analyzer) => GetOrCreateDescriptorsInfo(analyzer).TelemetryAllowed; + public bool TryGetDescriptorForDiagnosticId(string diagnosticId, [NotNullWhen(true)] out DiagnosticDescriptor? descriptor) + => _idToDescriptorsMap.TryGetValue(diagnosticId, out descriptor) && descriptor != null; + private DiagnosticDescriptorsInfo GetOrCreateDescriptorsInfo(DiagnosticAnalyzer analyzer) => _descriptorsInfo.GetValue(analyzer, CalculateDescriptorsInfo); - private static DiagnosticDescriptorsInfo CalculateDescriptorsInfo(DiagnosticAnalyzer analyzer) + private DiagnosticDescriptorsInfo CalculateDescriptorsInfo(DiagnosticAnalyzer analyzer) { ImmutableArray descriptors; try @@ -116,6 +128,7 @@ private static DiagnosticDescriptorsInfo CalculateDescriptorsInfo(DiagnosticAnal descriptors = ImmutableArray.Empty; } + PopulateIdToDescriptorMap(descriptors); var telemetryAllowed = IsTelemetryCollectionAllowed(analyzer, descriptors); return new DiagnosticDescriptorsInfo(descriptors, telemetryAllowed); } @@ -124,5 +137,27 @@ private static bool IsTelemetryCollectionAllowed(DiagnosticAnalyzer analyzer, Im => analyzer.IsCompilerAnalyzer() || analyzer is IBuiltInAnalyzer || descriptors.Length > 0 && descriptors[0].ImmutableCustomTags().Any(static t => t == WellKnownDiagnosticTags.Telemetry); + + private void PopulateIdToDescriptorMap(ImmutableArray descriptors) + { + foreach (var descriptor in descriptors) + { + if (!_idToDescriptorsMap.TryGetValue(descriptor.Id, out var existingDescriptor)) + { + _idToDescriptorsMap[descriptor.Id] = descriptor; + } + else if (existingDescriptor != null && !descriptor.Equals(existingDescriptor)) + { + // Multiple descriptors with same diagnostic ID, store null in the map. + // Exception case: Many CAxxxx analyzers use multiple descriptors with same ID which differ only in MessageFormat. + // This allows analyzer to report slightly differing diagnostic messages with same ID. + // We handle this case here by allowing existing descriptor to be used. + if (descriptor.WithMessageFormat(existingDescriptor.MessageFormat).Equals(existingDescriptor)) + continue; + + _idToDescriptorsMap[descriptor.Id] = null; + } + } + } } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index 2ee99aca89b96..36a816219c01d 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -352,7 +352,7 @@ private static DiagnosticSeverity GetEffectiveSeverity(ReportDiagnostic effectiv return DiagnosticSeverity.Warning; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingInvocationReasonsWrapper.cs b/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingInvocationReasonsWrapper.cs index bdf47a12111c4..74825b4f8ad1b 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingInvocationReasonsWrapper.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingInvocationReasonsWrapper.cs @@ -19,8 +19,8 @@ internal readonly struct UnitTestingInvocationReasonsWrapper public static readonly UnitTestingInvocationReasonsWrapper PredefinedSyntaxChanged = new(PredefinedInvocationReasons.SyntaxChanged); public static readonly UnitTestingInvocationReasonsWrapper PredefinedProjectConfigurationChanged = new(PredefinedInvocationReasons.ProjectConfigurationChanged); public static readonly UnitTestingInvocationReasonsWrapper PredefinedDocumentOpened = new(PredefinedInvocationReasons.DocumentOpened); - public static readonly UnitTestingInvocationReasonsWrapper PredefinedDocumentRemoved = new(PredefinedInvocationReasons.DocumentRemoved); public static readonly UnitTestingInvocationReasonsWrapper PredefinedDocumentClosed = new(PredefinedInvocationReasons.DocumentClosed); + public static readonly UnitTestingInvocationReasonsWrapper PredefinedDocumentRemoved = new(PredefinedInvocationReasons.DocumentRemoved); public static readonly UnitTestingInvocationReasonsWrapper PredefinedHighPriority = new(PredefinedInvocationReasons.HighPriority); public static readonly UnitTestingInvocationReasonsWrapper PredefinedProjectParseOptionsChanged = new(PredefinedInvocationReasons.ProjectParseOptionsChanged); public static readonly UnitTestingInvocationReasonsWrapper PredefinedSolutionRemoved = new(PredefinedInvocationReasons.SolutionRemoved); diff --git a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs index 0bc3da8f90d79..b248524189590 100644 --- a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs +++ b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs @@ -31,15 +31,22 @@ public ExtractMethodOptions() /// Combines global with document specific code generation options. /// [DataContract] -internal readonly record struct ExtractMethodGenerationOptions( - [property: DataMember] CodeGenerationOptions CodeGenerationOptions) +internal readonly record struct ExtractMethodGenerationOptions { + [DataMember] public required CodeGenerationOptions CodeGenerationOptions { get; init; } [DataMember] public ExtractMethodOptions ExtractOptions { get; init; } = ExtractMethodOptions.Default; [DataMember] public AddImportPlacementOptions AddImportOptions { get; init; } = AddImportPlacementOptions.Default; [DataMember] public LineFormattingOptions LineFormattingOptions { get; init; } = LineFormattingOptions.Default; public static ExtractMethodGenerationOptions GetDefault(LanguageServices languageServices) - => new(CodeGenerationOptions.GetDefault(languageServices)); + => new() + { + CodeGenerationOptions = CodeGenerationOptions.GetDefault(languageServices) + }; + + public ExtractMethodGenerationOptions() + { + } } internal static class ExtractMethodGenerationOptionsProviders @@ -48,16 +55,12 @@ public static async ValueTask GetExtractMethodGe { fallbackOptions ??= ExtractMethodGenerationOptions.GetDefault(document.Project.Services); - var extractOptions = fallbackOptions.Value.ExtractOptions; - var codeGenerationOptions = await document.GetCodeGenerationOptionsAsync(fallbackOptions.Value.CodeGenerationOptions, cancellationToken).ConfigureAwait(false); - var addImportOptions = await document.GetAddImportPlacementOptionsAsync(fallbackOptions.Value.AddImportOptions, cancellationToken).ConfigureAwait(false); - var lineFormattingOptions = await document.GetLineFormattingOptionsAsync(fallbackOptions.Value.LineFormattingOptions, cancellationToken).ConfigureAwait(false); - - return new ExtractMethodGenerationOptions(codeGenerationOptions) + return new ExtractMethodGenerationOptions() { - ExtractOptions = extractOptions, - AddImportOptions = addImportOptions, - LineFormattingOptions = lineFormattingOptions, + CodeGenerationOptions = await document.GetCodeGenerationOptionsAsync(fallbackOptions.Value.CodeGenerationOptions, cancellationToken).ConfigureAwait(false), + ExtractOptions = fallbackOptions.Value.ExtractOptions, + AddImportOptions = await document.GetAddImportPlacementOptionsAsync(fallbackOptions.Value.AddImportOptions, cancellationToken).ConfigureAwait(false), + LineFormattingOptions = await document.GetLineFormattingOptionsAsync(fallbackOptions.Value.LineFormattingOptions, cancellationToken).ConfigureAwait(false), }; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs index 66b1cba42613b..588f3fb7f9046 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder.cs @@ -107,7 +107,7 @@ private static async Task AddMetadataDeclarationsWithNormalQueryAsync( if (reference != null) { var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( - project.Solution, reference, cancellationToken).ConfigureAwait(false); + project.Solution, reference, checksum: null, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(info); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs index 579f17ce3c9ec..2fe08d5457b4c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs @@ -412,7 +412,7 @@ private static async Task AddMatchingMetadataTypesInMetadataReferenceAsync( // might get false positives. But that's fine as we still use 'tpeMatches' to make sure the match is // correct. var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( - project.Solution, reference, cancellationToken).ConfigureAwait(false); + project.Solution, reference, checksum: null, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(symbolTreeInfo); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs index ec17b61a5624b..75242a54e98e8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs @@ -60,7 +60,7 @@ public ValueTask OnDefinitionFoundAsync(SymbolGroup group, CancellationToken can } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs index bfaab33a80e04..4a9eac28592b0 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs @@ -48,11 +48,5 @@ ValueTask> FindSolutionSourceDecl ValueTask> FindProjectSourceDeclarationsWithPatternAsync( Checksum solutionChecksum, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); - - // Notification so we can keep the remote SymbolTreeInfoCache up to date. - - ValueTask AnalyzeDocumentAsync(Checksum solutionChecksum, DocumentId documentId, bool isMethodBodyEdit, CancellationToken cancellationToken); - ValueTask AnalyzeProjectAsync(Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); - ValueTask RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 2df78993fa529..e1bc02e280ca9 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -3,13 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; @@ -19,7 +16,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class AbstractSyntaxIndex : IObjectWritable { private static readonly string s_persistenceName = typeof(TIndex).Name; - private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("31"); + private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("33"); /// /// Cache of ParseOptions to a checksum for the contained @@ -89,7 +86,7 @@ internal partial class AbstractSyntaxIndex : IObjectWritable // to make the final checksum. // // Note: this intentionally ignores *other* ParseOption changes. This may look like it could cause us to - // get innacurate results, but here's why it's ok. The other ParseOption changes include: + // get inaccurate results, but here's why it's ok. The other ParseOption changes include: // // 1. LanguageVersion changes. It's ok to ignore that as for practically all language versions we don't // produce different trees. And, while there are some lang versions that produce different trees (for diff --git a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs index 092a4bb0f8546..005c24ffff2f5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs @@ -75,7 +75,7 @@ public ValueTask OnDefinitionFoundAsync(SymbolGroup group, CancellationToken can } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/ISymbolTreeInfoCacheService.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/ISymbolTreeInfoCacheService.cs new file mode 100644 index 0000000000000..1576ab28280d7 --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/ISymbolTreeInfoCacheService.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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree +{ + /// + /// Computes and caches indices for the source symbols in s and + /// for metadata symbols in s. + /// + internal interface ISymbolTreeInfoCacheService : IWorkspaceService + { + ValueTask TryGetPotentiallyStaleSourceSymbolTreeInfoAsync(Project project, CancellationToken cancellationToken); + ValueTask TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(Project project, PortableExecutableReference reference, CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.MetadataInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs similarity index 95% rename from src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.MetadataInfo.cs rename to src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs index e59cca2fb5c6b..a540d1dc98e7b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.MetadataInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs @@ -7,7 +7,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree { - internal sealed partial class SymbolTreeInfoCacheService + internal sealed partial class SymbolTreeInfoCacheServiceFactory { private readonly struct MetadataInfo { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 0e2cb5ffc5d25..f8635af1c7d95 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -7,14 +7,11 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; @@ -32,14 +29,15 @@ namespace Microsoft.CodeAnalysis.FindSymbols /// internal partial class SymbolTreeInfo : IChecksummedObject { + private static readonly StringComparer s_caseInsensitiveComparer = + CaseInsensitiveComparison.Comparer; + public Checksum Checksum { get; } /// - /// The list of nodes that represent symbols. The primary key into the sorting of this - /// list is the name. They are sorted case-insensitively with the . - /// Finding case-sensitive matches can be found by binary searching for something that - /// matches insensitively, and then searching around that equivalence class for one that - /// matches. + /// The list of nodes that represent symbols. The primary key into the sorting of this list is the name. They + /// are sorted case-insensitively . Finding case-sensitive matches can be found by binary searching for + /// something that matches insensitively, and then searching around that equivalence class for one that matches. /// private readonly ImmutableArray _nodes; @@ -74,25 +72,6 @@ public MultiDictionary.ValueSet GetExtensionMethodI private readonly SpellChecker _spellChecker; - private static readonly StringSliceComparer s_caseInsensitiveComparer = - StringSliceComparer.OrdinalIgnoreCase; - - // We first sort in a case insensitive manner. But, within items that match insensitively, - // we then sort in a case sensitive manner. This helps for searching as we'll walk all - // the items of a specific casing at once. This way features can cache values for that - // casing and reuse them. i.e. if we didn't do this we might get "Prop, prop, Prop, prop" - // which might cause other features to continually recalculate if that string matches what - // they're searching for. However, with this sort of comparison we now get - // "prop, prop, Prop, Prop". Features can take advantage of that by caching their previous - // result and reusing it when they see they're getting the same string again. - private static readonly Comparison s_totalComparer = (s1, s2) => - { - var diff = CaseInsensitiveComparison.Comparer.Compare(s1, s2); - return diff != 0 - ? diff - : StringComparer.Ordinal.Compare(s1, s2); - }; - private SymbolTreeInfo( Checksum checksum, ImmutableArray sortedNodes, @@ -122,7 +101,7 @@ private SymbolTreeInfo( public static SymbolTreeInfo CreateEmpty(Checksum checksum) { var unsortedNodes = ImmutableArray.Create(BuilderNode.RootNode); - SortNodes(unsortedNodes, out var sortedNodes); + var sortedNodes = SortNodes(unsortedNodes); return new SymbolTreeInfo(checksum, sortedNodes, CreateSpellChecker(checksum, sortedNodes), @@ -132,6 +111,9 @@ public static SymbolTreeInfo CreateEmpty(Checksum checksum) public SymbolTreeInfo WithChecksum(Checksum checksum) { + if (checksum == this.Checksum) + return this; + return new SymbolTreeInfo( checksum, _nodes, _spellChecker, _inheritanceMap, _receiverTypeNameToExtensionMethodMap); } @@ -209,76 +191,46 @@ private async Task> FindAsync( bool ignoreCase, CancellationToken cancellationToken) { - var comparer = GetComparer(ignoreCase); - IAssemblySymbol? assemblySymbol = null; - using var results = TemporaryArray.Empty; - foreach (var node in FindNodeIndices(name, comparer)) + + var (startIndexInclusive, endIndexExclusive) = FindCaseInsensitiveNodeIndices(_nodes, name); + + IAssemblySymbol? assemblySymbol = null; + for (var index = startIndexInclusive; index < endIndexExclusive; index++) { - cancellationToken.ThrowIfCancellationRequested(); - assemblySymbol ??= await lazyAssembly.GetValueAsync(cancellationToken).ConfigureAwait(false); + var node = _nodes[index]; - Bind(node, assemblySymbol.GlobalNamespace, ref results.AsRef(), cancellationToken); + // The find-operation found the case-insensitive range of results. So if the caller wants + // case-insensitive, then just check all of them. If they caller wants case-sensitive, then + // actually check that the node matches case-sensitively + if (ignoreCase || StringComparer.Ordinal.Equals(name, node.Name)) + { + assemblySymbol ??= await lazyAssembly.GetValueAsync(cancellationToken).ConfigureAwait(false); + Bind(index, assemblySymbol.GlobalNamespace, ref results.AsRef(), cancellationToken); + } } return results.ToImmutableAndClear(); } - private static StringSliceComparer GetComparer(bool ignoreCase) - { - return ignoreCase - ? StringSliceComparer.OrdinalIgnoreCase - : StringSliceComparer.Ordinal; - } - - private IEnumerable FindNodeIndices(string name, StringSliceComparer comparer) - => FindNodeIndices(_nodes, name, comparer); - - /// - /// Gets all the node indices with matching names per the . - /// - private static IEnumerable FindNodeIndices( - ImmutableArray nodes, - string name, StringSliceComparer comparer) + private static (int startIndexInclusive, int endIndexExclusive) FindCaseInsensitiveNodeIndices( + ImmutableArray nodes, string name) { // find any node that matches case-insensitively var startingPosition = BinarySearch(nodes, name); - var nameSlice = name.AsMemory(); - if (startingPosition != -1) - { - // yield if this matches by the actual given comparer - if (comparer.Equals(nameSlice, GetNameSlice(nodes, startingPosition))) - { - yield return startingPosition; - } + if (startingPosition == -1) + return default; - var position = startingPosition; - while (position > 0 && s_caseInsensitiveComparer.Equals(GetNameSlice(nodes, position - 1), nameSlice)) - { - position--; - if (comparer.Equals(GetNameSlice(nodes, position), nameSlice)) - { - yield return position; - } - } + var startIndex = startingPosition; + while (startIndex > 0 && s_caseInsensitiveComparer.Equals(nodes[startIndex - 1].Name, name)) + startIndex--; - position = startingPosition; - while (position + 1 < nodes.Length && s_caseInsensitiveComparer.Equals(GetNameSlice(nodes, position + 1), nameSlice)) - { - position++; - if (comparer.Equals(GetNameSlice(nodes, position), nameSlice)) - { - yield return position; - } - } - } - } + var endIndex = startingPosition; + while (endIndex + 1 < nodes.Length && s_caseInsensitiveComparer.Equals(nodes[endIndex + 1].Name, name)) + endIndex++; - private static ReadOnlyMemory GetNameSlice( - ImmutableArray nodes, int nodeIndex) - { - return nodes[nodeIndex].Name.AsMemory(); + return (startIndex, endIndex + 1); } private int BinarySearch(string name) @@ -289,7 +241,6 @@ private int BinarySearch(string name) /// private static int BinarySearch(ImmutableArray nodes, string name) { - var nameSlice = name.AsMemory(); var max = nodes.Length - 1; var min = 0; @@ -297,8 +248,7 @@ private static int BinarySearch(ImmutableArray nodes, string name) { var mid = min + ((max - min) >> 1); - var comparison = s_caseInsensitiveComparer.Compare( - GetNameSlice(nodes, mid), nameSlice); + var comparison = s_caseInsensitiveComparer.Compare(nodes[mid].Name, name); if (comparison < 0) { min = mid + 1; @@ -318,44 +268,29 @@ private static int BinarySearch(ImmutableArray nodes, string name) #region Construction - /// - /// Cache the symbol tree infos for assembly symbols that share the same underlying metadata. Generating symbol - /// trees for metadata can be expensive (in large metadata cases). And it's common for us to have many threads - /// to want to search the same metadata simultaneously. As such, we use an AsyncLazy to compute the value that - /// can be shared among all callers. - /// - private static readonly ConditionalWeakTable> s_metadataIdToInfo = new(); - private static SpellChecker CreateSpellChecker(Checksum checksum, ImmutableArray sortedNodes) - => new(checksum, sortedNodes.Select(n => n.Name.AsMemory())); + => new(checksum, sortedNodes.Select(n => n.Name)); - private static void SortNodes( - ImmutableArray unsortedNodes, - out ImmutableArray sortedNodes) + private static ImmutableArray SortNodes(ImmutableArray unsortedNodes) { // Generate index numbers from 0 to Count-1 - var tmp = new int[unsortedNodes.Length]; - for (var i = 0; i < tmp.Length; i++) - { + using var _1 = ArrayBuilder.GetInstance(unsortedNodes.Length, out var tmp); + tmp.Count = unsortedNodes.Length; + for (var i = 0; i < tmp.Count; i++) tmp[i] = i; - } // Sort the index according to node elements - Array.Sort(tmp, (a, b) => CompareNodes(unsortedNodes[a], unsortedNodes[b], unsortedNodes)); + tmp.Sort((a, b) => CompareNodes(unsortedNodes[a], unsortedNodes[b], unsortedNodes)); // Use the sort order to build the ranking table which will // be used as the map from original (unsorted) location to the // sorted location. - var ranking = new int[unsortedNodes.Length]; - for (var i = 0; i < tmp.Length; i++) - { + using var _2 = ArrayBuilder.GetInstance(unsortedNodes.Length, out var ranking); + ranking.Count = unsortedNodes.Length; + for (var i = 0; i < tmp.Count; i++) ranking[tmp[i]] = i; - } - // No longer need the tmp array - tmp = null; - - var result = ArrayBuilder.GetInstance(unsortedNodes.Length); + using var _3 = ArrayBuilder.GetInstance(unsortedNodes.Length, out var result); result.Count = unsortedNodes.Length; string? lastName = null; @@ -380,13 +315,13 @@ private static void SortNodes( lastName = currentName; } - sortedNodes = result.ToImmutableAndFree(); + return result.ToImmutableAndClear(); } private static int CompareNodes( BuilderNode x, BuilderNode y, ImmutableArray nodeList) { - var comp = s_totalComparer(x.Name, y.Name); + var comp = TotalComparer(x.Name, y.Name); if (comp == 0) { if (x.ParentIndex != y.ParentIndex) @@ -407,6 +342,22 @@ private static int CompareNodes( } return comp; + + // We first sort in a case insensitive manner. But, within items that match insensitively, + // we then sort in a case sensitive manner. This helps for searching as we'll walk all + // the items of a specific casing at once. This way features can cache values for that + // casing and reuse them. i.e. if we didn't do this we might get "Prop, prop, Prop, prop" + // which might cause other features to continually recalculate if that string matches what + // they're searching for. However, with this sort of comparison we now get + // "prop, prop, Prop, Prop". Features can take advantage of that by caching their previous + // result and reusing it when they see they're getting the same string again. + static int TotalComparer(string s1, string s2) + { + var diff = CaseInsensitiveComparison.Comparer.Compare(s1, s2); + return diff != 0 + ? diff + : StringComparer.Ordinal.Compare(s1, s2); + } } #endregion @@ -481,7 +432,7 @@ private static SymbolTreeInfo CreateSymbolTreeInfo( OrderPreservingMultiDictionary inheritanceMap, MultiDictionary? receiverTypeNameToExtensionMethodMap) { - SortNodes(unsortedNodes, out var sortedNodes); + var sortedNodes = SortNodes(unsortedNodes); var spellChecker = CreateSpellChecker(checksum, sortedNodes); return new SymbolTreeInfo( @@ -492,8 +443,6 @@ private static OrderPreservingMultiDictionary CreateIndexBasedInherita ImmutableArray nodes, OrderPreservingMultiDictionary inheritanceMap) { - // All names in metadata will be case sensitive. - var comparer = GetComparer(ignoreCase: false); var result = new OrderPreservingMultiDictionary(); foreach (var (baseName, derivedNames) in inheritanceMap) @@ -503,9 +452,14 @@ private static OrderPreservingMultiDictionary CreateIndexBasedInherita foreach (var derivedName in derivedNames) { - foreach (var derivedNameIndex in FindNodeIndices(nodes, derivedName, comparer)) + var (startIndexInclusive, endIndexExclusive) = FindCaseInsensitiveNodeIndices(nodes, derivedName); + + for (var derivedNameIndex = startIndexInclusive; derivedNameIndex < endIndexExclusive; derivedNameIndex++) { - result.Add(baseNameIndex, derivedNameIndex); + var node = nodes[derivedNameIndex]; + // All names in metadata will be case sensitive. + if (StringComparer.Ordinal.Equals(derivedName, node.Name)) + result.Add(baseNameIndex, derivedNameIndex); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs index 34bf954c37213..b1510a0680cb9 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs @@ -5,54 +5,82 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Composition; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree +namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree; + +internal sealed partial class SymbolTreeInfoCacheServiceFactory { - /// - /// Computes and caches indices for the source symbols in s and - /// for metadata symbols in s. - /// - [ExportWorkspaceService(typeof(SymbolTreeInfoCacheService)), Shared] - internal sealed partial class SymbolTreeInfoCacheService : IWorkspaceService + internal sealed partial class SymbolTreeInfoCacheService : ISymbolTreeInfoCacheService, IDisposable { - private readonly ConcurrentDictionary _projectIdToInfo = new(); - private readonly ConcurrentDictionary _metadataIdToInfo = new(); + /// + /// Same value as SolutionCrawlerTimeSpan.EntireProjectWorkerBackOff + /// + private static readonly TimeSpan EntireProjectWorkerBackOff = TimeSpan.FromMilliseconds(5000); + + private static readonly TaskScheduler s_exclusiveScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SymbolTreeInfoCacheService() + private readonly ConcurrentDictionary _projectIdToInfo = new(); + private readonly ConcurrentDictionary _peReferenceToInfo = new(); + + private readonly CancellationTokenSource _tokenSource = new(); + + private readonly Workspace _workspace; + private readonly AsyncBatchingWorkQueue _workQueue; + + /// + /// Scheduler to run our tasks on. If we're in the remote host , we'll run all our tasks concurrently. + /// Otherwise, we will run them serially using + /// + private readonly TaskScheduler _scheduler; + + public SymbolTreeInfoCacheService(Workspace workspace, IAsynchronousOperationListener listener) { + _workspace = workspace; + _workQueue = new AsyncBatchingWorkQueue( + EntireProjectWorkerBackOff, + ProcessProjectsAsync, + EqualityComparer.Default, + listener, + _tokenSource.Token); + + _scheduler = workspace.Kind == WorkspaceKind.RemoteWorkspace ? TaskScheduler.Default : s_exclusiveScheduler; } + void IDisposable.Dispose() + => _tokenSource.Cancel(); + + private Task CreateWorkAsync(Func createWorkAsync, CancellationToken cancellationToken) + => Task.Factory.StartNew(createWorkAsync, cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap(); + /// - /// Gets the latest computed for the requested . This - /// may return an index corresponding to a prior version of the refernce if it has since changed. Another - /// system is responsible for bringing these indices up to date in the background. + /// Gets the latest computed for the requested . + /// This may return an index corresponding to a prior version of the reference if it has since changed. + /// Another system is responsible for bringing these indices up to date in the background. /// public async ValueTask TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync( - Solution solution, + Project project, PortableExecutableReference reference, CancellationToken cancellationToken) { - var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference); - if (metadataId == null) + if (!project.SupportsCompilation) return null; + // Kick off the work to update the data we have for this project. + _workQueue.AddWork(project.Id); + // See if the last value produced exactly matches what the caller is asking for. If so, return that. - if (_metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo)) + if (_peReferenceToInfo.TryGetValue(reference, out var metadataInfo)) return metadataInfo.SymbolTreeInfo; // If we didn't have it in our cache, see if we can load it from disk. + var solution = project.Solution; var info = await SymbolTreeInfo.LoadAnyInfoForMetadataReferenceAsync(solution, reference, cancellationToken).ConfigureAwait(false); if (info is null) return null; @@ -60,145 +88,127 @@ public SymbolTreeInfoCacheService() var referencingProjects = new HashSet(solution.Projects.Where(p => p.MetadataReferences.Contains(reference)).Select(p => p.Id)); // attempt to add this item to the map. But defer to whatever is in the map now if something else beat us to this. - return _metadataIdToInfo.GetOrAdd(metadataId, new MetadataInfo(info, referencingProjects)).SymbolTreeInfo; + return _peReferenceToInfo.GetOrAdd(reference, new MetadataInfo(info, referencingProjects)).SymbolTreeInfo; } - public async Task TryGetPotentiallyStaleSourceSymbolTreeInfoAsync( + public async ValueTask TryGetPotentiallyStaleSourceSymbolTreeInfoAsync( Project project, CancellationToken cancellationToken) { + if (!project.SupportsCompilation) + return null; + + // Kick off the work to update the data we have for this project. + _workQueue.AddWork(project.Id); + // See if the last value produced exactly matches what the caller is asking for. If so, return that. if (_projectIdToInfo.TryGetValue(project.Id, out var projectInfo)) - return projectInfo; + return projectInfo.info; // If we didn't have it in our cache, see if we can load some version of it from disk. var info = await SymbolTreeInfo.LoadAnyInfoForSourceAssemblyAsync(project, cancellationToken).ConfigureAwait(false); if (info is null) return null; - // attempt to add this item to the map. But defer to whatever is in the map now if something else beat us to this. - return _projectIdToInfo.GetOrAdd(project.Id, info); + // attempt to add this item to the map. But defer to whatever is in the map now if something else beat + // us to this. Don't provide a version here so that the next time we update this data it will get + // overwritten with the latest computed data. + return _projectIdToInfo.GetOrAdd(project.Id, (semanticVersion: default, info)).info; } - public async Task AnalyzeDocumentAsync(Document document, bool isMethodBodyEdit, CancellationToken cancellationToken) + private async ValueTask ProcessProjectsAsync( + ImmutableSegmentedList projectIds, CancellationToken cancellationToken) { - if (!document.Project.SupportsCompilation) - return; - - // This was a method body edit. We can reuse the existing SymbolTreeInfo if we have one. We can't just - // bail out here as the change in the document means we'll have a new checksum. We need to get that new - // checksum so that our cached information is valid. - if (isMethodBodyEdit && - _projectIdToInfo.TryGetValue(document.Project.Id, out var cachedInfo)) + var solution = _workspace.CurrentSolution; + + foreach (var projectId in projectIds) { - var checksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync( - document.Project, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + + var project = solution.GetProject(projectId); + if (project is not { SupportsCompilation: true }) + continue; - var newInfo = cachedInfo.WithChecksum(checksum); - _projectIdToInfo[document.Project.Id] = newInfo; - return; + // NOTE: currently we process projects serially. This is because the same metadata reference might be + // found in multiple projects and we can't currently process that in parallel. + await AnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); } - await AnalyzeProjectAsync(document.Project, cancellationToken).ConfigureAwait(false); + // Now that we've produced all the indices for the projects asked for, also remove any indices for projects + // no longer in the solution. + var removedProjectIds = _projectIdToInfo.Keys.Except(solution.ProjectIds).ToArray(); + foreach (var projectId in removedProjectIds) + this.RemoveProject(projectId); } - public async Task AnalyzeProjectAsync(Project project, CancellationToken cancellationToken) + private async Task AnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { - if (!project.SupportsCompilation) - return; - - // Produce the indices for the source and metadata symbols in parallel. using var _ = ArrayBuilder.GetInstance(out var tasks); - tasks.Add(Task.Run(() => this.UpdateSourceSymbolTreeInfoAsync(project, cancellationToken), cancellationToken)); - tasks.Add(Task.Run(() => this.UpdateReferencesAsync(project, cancellationToken), cancellationToken)); + // We can compute source-symbols in parallel with the metadata symbols. + tasks.Add(CreateWorkAsync(() => this.UpdateSourceSymbolTreeInfoAsync(project, cancellationToken), cancellationToken)); + + // Add tasks to update the symboltree for all metadata references. As these are all distinct, they can run + // in parallel as we won't be trying to update the associated data for the same reference at the same time. + foreach (var reference in project.MetadataReferences.OfType().Distinct()) + tasks.Add(CreateWorkAsync(() => UpdateReferenceAsync(project, reference, cancellationToken), cancellationToken)); await Task.WhenAll(tasks).ConfigureAwait(false); } private async Task UpdateSourceSymbolTreeInfoAsync(Project project, CancellationToken cancellationToken) { - var checksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false); + // Find the top-level-semantic-version of this project. We only want to recompute if it has changed. This is + // because the symboltree contains the names of the types/namespaces in the project and would not change + // if the semantic-version of the project hasn't changed. We also do not need to check the 'dependent + // version'. As this is just tracking parent/child relationships of namespace/type names for the source + // types in this project, this cannot change based on what happens in other projects. + var semanticVersion = await project.GetSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + if (!_projectIdToInfo.TryGetValue(project.Id, out var projectInfo) || - projectInfo.Checksum != checksum) + projectInfo.semanticVersion != semanticVersion) { - projectInfo = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( - project, cancellationToken).ConfigureAwait(false); + // If the checksum is the same (which can happen if we loaded the previous index from disk), then no + // need to recompute. + var checksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false); + if (projectInfo.info?.Checksum != checksum) + { + // Otherwise, looks like things changed. Compute and persist the latest index. + var info = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( + project, checksum, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(projectInfo); - Contract.ThrowIfTrue(projectInfo.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); + Contract.ThrowIfNull(info); + Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); - // Mark that we're up to date with this project. Future calls with the same - // semantic version can bail out immediately. - _projectIdToInfo[project.Id] = projectInfo; + // Mark that we're up to date with this project. Future calls with the same semantic-version or + // checksum can bail out immediately. + _projectIdToInfo[project.Id] = (semanticVersion, info); + } } } - private async Task UpdateReferencesAsync(Project project, CancellationToken cancellationToken) + private async Task UpdateReferenceAsync( + Project project, + PortableExecutableReference reference, + CancellationToken cancellationToken) { - // Process all metadata references. If it remote workspace, do this in parallel. - using var pendingTasks = new TemporaryArray(); - - foreach (var reference in project.MetadataReferences) + var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution.Services, reference, cancellationToken); + if (!_peReferenceToInfo.TryGetValue(reference, out var metadataInfo) || + metadataInfo.SymbolTreeInfo.Checksum != checksum) { - if (reference is not PortableExecutableReference portableExecutableReference) - continue; + var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( + project.Solution, reference, checksum, cancellationToken).ConfigureAwait(false); - if (cancellationToken.IsCancellationRequested) - { - // Break out of this loop to make sure other pending operations process cancellation before - // returning. - break; - } + Contract.ThrowIfNull(info); + Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); - var updateTask = UpdateReferenceAsync(_metadataIdToInfo, project, portableExecutableReference, cancellationToken); - if (updateTask.Status != TaskStatus.RanToCompletion) - pendingTasks.Add(updateTask); + metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet()); + _peReferenceToInfo[reference] = metadataInfo; } - if (pendingTasks.Count > 0) + // Keep track that this dll is referenced by this project. + lock (metadataInfo.ReferencingProjects) { - // If any update operations did not complete synchronously (including any cancelled operations), - // wait for them to complete now. - await Task.WhenAll(pendingTasks.ToImmutableAndClear()).ConfigureAwait(false); - } - - // ⚠ This local function must be 'async' to ensure exceptions are captured in the resulting task and - // not thrown directly to the caller. - static async Task UpdateReferenceAsync( - ConcurrentDictionary metadataIdToInfo, - Project project, - PortableExecutableReference reference, - CancellationToken cancellationToken) - { - var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference); - if (metadataId == null) - return; - - // 🐉 PERF: GetMetadataChecksum indirectly uses a ConditionalWeakTable. This call is intentionally - // placed before the first 'await' of this asynchronous method to ensure it executes in the - // synchronous portion of the caller. https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1270250 - var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution.Services, reference, cancellationToken); - if (!metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) || - metadataInfo.SymbolTreeInfo.Checksum != checksum) - { - var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync( - project.Solution, reference, cancellationToken: cancellationToken).ConfigureAwait(false); - - Contract.ThrowIfNull(info); - Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum."); - - // Note, getting the info may fail (for example, bogus metadata). That's ok. - // We still want to cache that result so that don't try to continuously produce - // this info over and over again. - metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet()); - metadataIdToInfo[metadataId] = metadataInfo; - } - - // Keep track that this dll is referenced by this project. - lock (metadataInfo.ReferencingProjects) - { - metadataInfo.ReferencingProjects.Add(project.Id); - } + metadataInfo.ReferencingProjects.Add(project.Id); } } @@ -210,7 +220,7 @@ public void RemoveProject(ProjectId projectId) private void RemoveMetadataReferences(ProjectId projectId) { - foreach (var (id, info) in _metadataIdToInfo.ToArray()) + foreach (var (reference, info) in _peReferenceToInfo.ToArray()) { lock (info.ReferencingProjects) { @@ -218,9 +228,30 @@ private void RemoveMetadataReferences(ProjectId projectId) // If this metadata dll isn't referenced by any project. We can just dump it. if (info.ReferencingProjects.Count == 0) - _metadataIdToInfo.TryRemove(id, out _); + _peReferenceToInfo.TryRemove(reference, out _); } } } + + public TestAccessor GetTestAccessor() + => new(this); + + public struct TestAccessor + { + private readonly SymbolTreeInfoCacheService _services; + + public TestAccessor(SymbolTreeInfoCacheService service) + { + _services = service; + } + + public Task AnalyzeSolutionAsync() + { + foreach (var projectId in _services._workspace.CurrentSolution.ProjectIds) + _services._workQueue.AddWork(projectId); + + return _services._workQueue.WaitUntilCurrentBatchCompletesAsync(); + } + } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheServiceFactory.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheServiceFactory.cs new file mode 100644 index 0000000000000..85772f815bdf9 --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheServiceFactory.cs @@ -0,0 +1,28 @@ +// 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 Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree; + +[ExportWorkspaceServiceFactory(typeof(ISymbolTreeInfoCacheService)), Shared] +internal sealed partial class SymbolTreeInfoCacheServiceFactory : IWorkspaceServiceFactory +{ + private readonly IAsynchronousOperationListener _listener; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public SymbolTreeInfoCacheServiceFactory( + IAsynchronousOperationListenerProvider listenerProvider) + { + _listener = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy); + } + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => new SymbolTreeInfoCacheService(workspaceServices.Workspace, _listener); +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 72df9c0e86374..55966325c3d1a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -12,6 +12,7 @@ using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; @@ -26,6 +27,30 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { + /// + /// Cache the symbol tree infos for assembly symbols produced from a particular . Generating symbol trees for metadata can be expensive (in large + /// metadata cases). And it's common for us to have many threads to want to search the same metadata + /// simultaneously. As such, we use an AsyncLazy to compute the value that can be shared among all callers. + /// + /// We store this keyed off of the produced by . This + /// ensures that + /// + /// + private static readonly ConditionalWeakTable> s_peReferenceToInfo = new(); + + /// + /// Similar to except that this caches based on metadata id. The primary + /// difference here is that you can have the same MetadataId from two different s, while having different checksums. For example, if the aliases of a + /// are changed (see , then it will have a different + /// checksum, but same metadata ID. As such, we can use this table to ensure we only do the expensive + /// computation of the once per , but we may then have to + /// make a copy of it with a new if the checksums differ. + /// + private static readonly ConditionalWeakTable> s_metadataIdToSymbolTreeInfo = new(); + private static string GetMetadataNameWithoutBackticks(MetadataReader reader, StringHandle name) { var blobReader = reader.GetBlobReader(name); @@ -66,73 +91,98 @@ private static string GetMetadataNameWithoutBackticks(MetadataReader reader, Str } } - public static ValueTask GetInfoForMetadataReferenceAsync( - Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) - { - var checksum = GetMetadataChecksum(solution.Services, reference, cancellationToken); - return GetInfoForMetadataReferenceAsync(solution, reference, checksum, cancellationToken); - } - /// /// Produces a for a given . /// Note: will never return null; /// + /// Optional checksum for the (produced by ). Can be provided if already computed. If not provided it will be computed + /// and used for the . [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] - private static async ValueTask GetInfoForMetadataReferenceAsync( + public static async ValueTask GetInfoForMetadataReferenceAsync( Solution solution, PortableExecutableReference reference, - Checksum checksum, + Checksum? checksum, CancellationToken cancellationToken) { - var metadataId = GetMetadataIdNoThrow(reference); - if (metadataId == null) - return CreateEmpty(checksum); + checksum ??= GetMetadataChecksum(solution.Services, reference, cancellationToken); - if (s_metadataIdToInfo.TryGetValue(metadataId, out var infoTask)) + if (s_peReferenceToInfo.TryGetValue(reference, out var infoTask)) { var info = await infoTask.GetValueAsync(cancellationToken).ConfigureAwait(false); - if (info.Checksum == checksum) - return info; + Contract.ThrowIfTrue(info.Checksum != checksum, "How could the info stored for a particular PEReference now have a different checksum?"); + return info; } - var metadata = GetMetadataNoThrow(reference); - if (metadata == null) - return CreateEmpty(checksum); - return await GetInfoForMetadataReferenceSlowAsync( - solution.Services, SolutionKey.ToSolutionKey(solution), reference, metadata, cancellationToken).ConfigureAwait(false); + solution.Services, SolutionKey.ToSolutionKey(solution), reference, checksum, cancellationToken).ConfigureAwait(false); + + static async Task GetInfoForMetadataReferenceSlowAsync( + SolutionServices services, + SolutionKey solutionKey, + PortableExecutableReference reference, + Checksum checksum, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Important: this captured async lazy may live a long time *without* computing the final results. As + // such, it is important that it not capture any large state. For example, it should not hold onto a + // Solution instance. + // + // this is keyed per reference, so that have unique SymbolTreeInfo's per reference with their own + // correct checksum. Ensuring we only compute this once per *Metadata* instance though is handled below in + // CreateMetadataSymbolTreeInfoAsync + var asyncLazy = s_peReferenceToInfo.GetValue( + reference, + id => new AsyncLazy( + c => CreateMetadataSymbolTreeInfoAsync(services, solutionKey, reference, checksum, c), + cacheResult: true)); + + return await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); + } + + static async Task CreateMetadataSymbolTreeInfoAsync( + SolutionServices services, + SolutionKey solutionKey, + PortableExecutableReference reference, + Checksum checksum, + CancellationToken cancellationToken) + { + var metadataId = GetMetadataIdNoThrow(reference); + if (metadataId == null) + return CreateEmpty(checksum); + + var asyncLazy = s_metadataIdToSymbolTreeInfo.GetValue( + metadataId, + metadataId => new AsyncLazy( + cancellationToken => LoadOrCreateAsync( + services, + solutionKey, + checksum, + createAsync: checksum => new ValueTask(new MetadataInfoCreator(checksum, GetMetadataNoThrow(reference)).Create()), + keySuffix: GetMetadataKeySuffix(reference), + cancellationToken), + cacheResult: true)); + + var metadataIdSymbolTreeInfo = await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); + + // we got the info that was originally computed against this particular metadata-id. However, the same + // ID could be reused across different PEReferences/checksums (for example, a PEReference whose aliases + // were changed). As such, if this doesn't correspond to the same checksum, make a copy of this tree + // specific to the checksum we were asked for. + return metadataIdSymbolTreeInfo.WithChecksum(checksum); + } } public static async Task TryGetCachedInfoForMetadataReferenceIgnoreChecksumAsync(PortableExecutableReference reference, CancellationToken cancellationToken) { - var metadataId = GetMetadataIdNoThrow(reference); - if (metadataId == null || !s_metadataIdToInfo.TryGetValue(metadataId, out var infoTask)) + if (!s_peReferenceToInfo.TryGetValue(reference, out var infoTask)) return null; return await infoTask.GetValueAsync(cancellationToken).ConfigureAwait(false); } - private static async Task GetInfoForMetadataReferenceSlowAsync( - SolutionServices services, - SolutionKey solutionKey, - PortableExecutableReference reference, - Metadata metadata, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - // Important: this captured async lazy may live a long time *without* computing the final results. As such, - // it is important that it note capture any large state. For example, it should not hold onto a Solution - // instance. - var asyncLazy = s_metadataIdToInfo.GetValue( - metadata.Id, - id => new AsyncLazy( - c => CreateMetadataSymbolTreeInfoAsync(services, solutionKey, reference, c), - cacheResult: true)); - - return await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); - } - [PerformanceSensitive("https://github.com/dotnet/roslyn/issues/33131", AllowCaptures = false)] public static Checksum GetMetadataChecksum( SolutionServices services, PortableExecutableReference reference, CancellationToken cancellationToken) @@ -147,46 +197,31 @@ public static Checksum GetMetadataChecksum( // Break things up to the fast path above and this slow path where we allocate a closure. return GetMetadataChecksumSlow(services, reference, cancellationToken); - } - private static Checksum GetMetadataChecksumSlow(SolutionServices services, PortableExecutableReference reference, CancellationToken cancellationToken) - { - return ChecksumCache.GetOrCreate(reference, _ => + static Checksum GetMetadataChecksumSlow(SolutionServices services, PortableExecutableReference reference, CancellationToken cancellationToken) { - var serializer = services.GetRequiredService(); - var checksum = serializer.CreateChecksum(reference, cancellationToken); + return ChecksumCache.GetOrCreate(reference, _ => + { + var serializer = services.GetRequiredService(); + var checksum = serializer.CreateChecksum(reference, cancellationToken); - // Include serialization format version in our checksum. That way if the - // version ever changes, all persisted data won't match the current checksum - // we expect, and we'll recompute things. - return Checksum.Create(checksum, SerializationFormatChecksum); - }); + // Include serialization format version in our checksum. That way if the + // version ever changes, all persisted data won't match the current checksum + // we expect, and we'll recompute things. + return Checksum.Create(checksum, SerializationFormatChecksum); + }); + } } private static string GetMetadataKeySuffix(PortableExecutableReference reference) => "_Metadata_" + reference.FilePath; - private static Task CreateMetadataSymbolTreeInfoAsync( - SolutionServices services, - SolutionKey solutionKey, - PortableExecutableReference reference, - CancellationToken cancellationToken) - { - return LoadOrCreateAsync( - services, - solutionKey, - getChecksumAsync: () => new ValueTask(GetMetadataChecksum(services, reference, cancellationToken)), - createAsync: checksum => new ValueTask(new MetadataInfoCreator(checksum, reference).Create()), - keySuffix: GetMetadataKeySuffix(reference), - cancellationToken); - } - /// /// Loads any info we have for this reference from our persistence store. Will succeed regardless of the /// checksum of the . Should only be used by clients that are ok with potentially /// stale data. /// - public static Task LoadAnyInfoForMetadataReferenceAsync( + public static Task LoadAnyInfoForMetadataReferenceAsync( Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) @@ -206,7 +241,7 @@ private struct MetadataInfoCreator : IDisposable private static readonly ObjectPool> s_stringListPool = SharedPools.Default>(); private readonly Checksum _checksum; - private readonly PortableExecutableReference _reference; + private readonly Metadata? _metadata; private readonly OrderPreservingMultiDictionary _inheritanceMap; private readonly OrderPreservingMultiDictionary _parentToChildren; @@ -226,10 +261,10 @@ private struct MetadataInfoCreator : IDisposable private bool _containsExtensionsMethod; public MetadataInfoCreator( - Checksum checksum, PortableExecutableReference reference) + Checksum checksum, Metadata? metadata) { _checksum = checksum; - _reference = reference; + _metadata = metadata; _containsExtensionsMethod = false; _inheritanceMap = OrderPreservingMultiDictionary.GetInstance(); @@ -262,7 +297,7 @@ private static ImmutableArray GetModuleMetadata(Metadata? metada internal SymbolTreeInfo Create() { - foreach (var moduleMetadata in GetModuleMetadata(GetMetadataNoThrow(_reference))) + foreach (var moduleMetadata in GetModuleMetadata(_metadata)) { try { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 6b71f2efb3448..4fdc3e7fe2e79 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -2,13 +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. -#nullable disable - using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; @@ -23,7 +19,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class SymbolTreeInfo : IObjectWritable { private const string PrefixSymbolTreeInfo = ""; - private static readonly Checksum SerializationFormatChecksum = Checksum.Create("23"); + private static readonly Checksum SerializationFormatChecksum = Checksum.Create("24"); /// /// Generalized function for loading/creating/persisting data. Used as the common core code for serialization @@ -32,18 +28,13 @@ internal partial class SymbolTreeInfo : IObjectWritable private static async Task LoadOrCreateAsync( SolutionServices services, SolutionKey solutionKey, - Func> getChecksumAsync, + Checksum checksum, Func> createAsync, string keySuffix, CancellationToken cancellationToken) { - var checksum = await getChecksumAsync().ConfigureAwait(false); - using (Logger.LogBlock(FunctionId.SymbolTreeInfo_TryLoadOrCreate, cancellationToken)) { - if (checksum == null) - return await CreateWithLoggingAsync().ConfigureAwait(false); - // Ok, we can use persistence. First try to load from the persistence service. The data in the // persistence store must match the checksum passed in. @@ -59,8 +50,12 @@ private static async Task LoadOrCreateAsync( cancellationToken.ThrowIfCancellationRequested(); // Now, try to create a new instance and write it to the persistence service. - var result = await CreateWithLoggingAsync().ConfigureAwait(false); - Contract.ThrowIfNull(result); + SymbolTreeInfo result; + using (Logger.LogBlock(FunctionId.SymbolTreeInfo_Create, cancellationToken)) + { + result = await createAsync(checksum).ConfigureAwait(false); + Contract.ThrowIfNull(result); + } var persistentStorageService = services.GetPersistentStorageService(); @@ -82,17 +77,9 @@ private static async Task LoadOrCreateAsync( return result; } - - async Task CreateWithLoggingAsync() - { - using (Logger.LogBlock(FunctionId.SymbolTreeInfo_Create, cancellationToken)) - { - return await createAsync(checksum).ConfigureAwait(false); - } - } } - private static async Task LoadAsync( + private static async Task LoadAsync( SolutionServices services, SolutionKey solutionKey, Checksum checksum, @@ -194,9 +181,8 @@ static IEnumerable> GroupByName(ReadOnlyMemory sorted } } - private static SymbolTreeInfo TryReadSymbolTreeInfo( - ObjectReader reader, - Checksum checksum) + private static SymbolTreeInfo? TryReadSymbolTreeInfo( + ObjectReader reader, Checksum checksum) { if (reader == null) return null; @@ -204,12 +190,13 @@ private static SymbolTreeInfo TryReadSymbolTreeInfo( try { var nodeCount = reader.ReadInt32(); - var nodes = ArrayBuilder.GetInstance(nodeCount); - while (nodes.Count < nodeCount) + using var _ = ArrayBuilder.GetInstance(nodeCount, out var nodes); + + for (var i = 0; i < nodeCount; i++) { var name = reader.ReadString(); var groupCount = reader.ReadInt32(); - for (var i = 0; i < groupCount; i++) + for (var j = 0; j < groupCount; j++) { var parentIndex = reader.ReadInt32(); nodes.Add(new Node(name, parentIndex)); @@ -230,7 +217,7 @@ private static SymbolTreeInfo TryReadSymbolTreeInfo( } } - MultiDictionary receiverTypeNameToExtensionMethodMap; + MultiDictionary? receiverTypeNameToExtensionMethodMap; var keyCount = reader.ReadInt32(); if (keyCount == 0) @@ -256,14 +243,14 @@ private static SymbolTreeInfo TryReadSymbolTreeInfo( } } - var nodeArray = nodes.ToImmutableAndFree(); var spellChecker = SpellChecker.TryReadFrom(reader); if (spellChecker is null) return null; + var nodeArray = nodes.ToImmutableAndClear(); + return new SymbolTreeInfo( - checksum, nodeArray, spellChecker, inheritanceMap, - receiverTypeNameToExtensionMethodMap); + checksum, nodeArray, spellChecker, inheritanceMap, receiverTypeNameToExtensionMethodMap); } catch { @@ -275,11 +262,8 @@ private static SymbolTreeInfo TryReadSymbolTreeInfo( internal readonly partial struct TestAccessor { - internal static SymbolTreeInfo ReadSymbolTreeInfo( - ObjectReader reader, Checksum checksum) - { - return TryReadSymbolTreeInfo(reader, checksum); - } + public static SymbolTreeInfo? ReadSymbolTreeInfo(ObjectReader reader, Checksum checksum) + => TryReadSymbolTreeInfo(reader, checksum); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index ff25b55569baa..ceab6736cefb4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.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 System.Runtime.CompilerServices; using System.Threading; @@ -17,13 +18,12 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { - private static readonly SimplePool> s_symbolMapPool = - new(() => new MultiDictionary()); + private static readonly SimplePool> s_symbolMapPool = new(() => new()); - private static MultiDictionary AllocateSymbolMap() + private static MultiDictionary AllocateSymbolMap() => s_symbolMapPool.Allocate(); - private static void FreeSymbolMap(MultiDictionary symbolMap) + private static void FreeSymbolMap(MultiDictionary symbolMap) { symbolMap.Clear(); s_symbolMapPool.Free(symbolMap); @@ -33,14 +33,14 @@ private static string GetSourceKeySuffix(Project project) => "_Source_" + project.FilePath; public static Task GetInfoForSourceAssemblyAsync( - Project project, CancellationToken cancellationToken) + Project project, Checksum checksum, CancellationToken cancellationToken) { var solution = project.Solution; return LoadOrCreateAsync( solution.Services, SolutionKey.ToSolutionKey(solution), - getChecksumAsync: async () => await GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false), + checksum, createAsync: checksum => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), keySuffix: GetSourceKeySuffix(project), cancellationToken); @@ -122,32 +122,21 @@ internal static async ValueTask CreateSourceSymbolTreeInfoAsync( if (assembly == null) return CreateEmpty(checksum); - var unsortedNodes = ArrayBuilder.GetInstance(); - unsortedNodes.Add(new BuilderNode(assembly.GlobalNamespace.Name, RootNodeParentIndex)); - - GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate); - - return CreateSymbolTreeInfo( - checksum, - unsortedNodes.ToImmutableAndFree(), - inheritanceMap: new OrderPreservingMultiDictionary(), - receiverTypeNameToExtensionMethodMap: null); - } - - // generate nodes for the global namespace an all descendants - private static void GenerateSourceNodes( - INamespaceSymbol globalNamespace, - ArrayBuilder list, - Action> lookup) - { - // Add all child members var symbolMap = AllocateSymbolMap(); try { - lookup(globalNamespace, symbolMap); - - foreach (var (name, symbols) in symbolMap) - GenerateSourceNodes(name, 0 /*index of root node*/, symbols, list, lookup); + // generate nodes for the global namespace and all descendants + using var _ = ArrayBuilder.GetInstance(out var unsortedBuilderNodes); + + var globalNamespaceName = assembly.GlobalNamespace.Name; + symbolMap.Add(globalNamespaceName, assembly.GlobalNamespace); + GenerateSourceNodes(globalNamespaceName, RootNodeParentIndex, symbolMap[globalNamespaceName], unsortedBuilderNodes); + + return CreateSymbolTreeInfo( + checksum, + unsortedBuilderNodes.ToImmutable(), + inheritanceMap: new OrderPreservingMultiDictionary(), + receiverTypeNameToExtensionMethodMap: null); } finally { @@ -155,32 +144,53 @@ private static void GenerateSourceNodes( } } - private static readonly Func s_useSymbolNoPrivate = - s => s.CanBeReferencedByName && s.DeclaredAccessibility != Accessibility.Private; - - // generate nodes for symbols that share the same name, and all their descendants private static void GenerateSourceNodes( string name, int parentIndex, - MultiDictionary.ValueSet symbolsWithSameName, - ArrayBuilder list, - Action> lookup) + MultiDictionary.ValueSet symbolsWithSameName, + ArrayBuilder list) { + // Add the node for this name, and record which parent it points at. And keep track of the index of the + // node we just added. var node = new BuilderNode(name, parentIndex); var nodeIndex = list.Count; list.Add(node); var symbolMap = AllocateSymbolMap(); + using var _ = PooledHashSet.GetInstance(out var seenNames); try { - // Add all child members + // Walk the symbols with this name, and add all their child namespaces and types, grouping them together + // based on their name. There may be multiple (for example, Action, Action, etc.) foreach (var symbol in symbolsWithSameName) + AddChildNamespacesAndTypes(symbol, symbolMap); + + // Now, go through all those groups and make the single mapping from their name to the builder-node we + // just created above, and recurse into their children as well. + foreach (var (childName, childSymbols) in symbolMap) { - lookup(symbol, symbolMap); + seenNames.Add(childName); + GenerateSourceNodes(childName, nodeIndex, childSymbols, list); } - foreach (var (symbolName, symbols) in symbolMap) - GenerateSourceNodes(symbolName, nodeIndex, symbols, list, lookup); + // The above loops only create nodes for namespaces and types. we also want nodes for members as well. + // However, we do not want to force the symbols for those members to be created just to get the names. + // + // So walk through the symbols again, and for the named-types grab all the member-names contained + // therein. If we didn't already see that child name when recursing above, then make a builder-node for + // it that points to the builder-node we just created above. + + foreach (var symbol in symbolsWithSameName) + { + if (symbol is INamedTypeSymbol namedType) + { + foreach (var childMemberName in namedType.MemberNames) + { + if (seenNames.Add(childMemberName)) + list.Add(new BuilderNode(childMemberName, nodeIndex)); + } + } + } } finally { @@ -188,20 +198,19 @@ private static void GenerateSourceNodes( } } - private static readonly Action> s_getMembersNoPrivate = - (symbol, symbolMap) => AddSymbol(symbol, symbolMap, s_useSymbolNoPrivate); - - private static void AddSymbol(ISymbol symbol, MultiDictionary symbolMap, Func useSymbol) + private static void AddChildNamespacesAndTypes(INamespaceOrTypeSymbol symbol, MultiDictionary symbolMap) { - if (symbol is INamespaceOrTypeSymbol nt) + if (symbol is INamespaceSymbol namespaceSymbol) { - foreach (var member in nt.GetMembers()) - { - if (useSymbol(member)) - { - symbolMap.Add(member.Name, member); - } - } + foreach (var childNamespaceOrType in namespaceSymbol.GetMembers()) + symbolMap.Add(childNamespaceOrType.Name, childNamespaceOrType); + } + else if (symbol is INamedTypeSymbol namedTypeSymbol) + { + // for named-types, we only need to recurse into child types. Call GetTypeMembers instead of GetMembers + // so we do not cause all child symbols to be created. + foreach (var childType in namedTypeSymbol.GetTypeMembers()) + symbolMap.Add(childType.Name, childType); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/DeclaredSymbolInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/DeclaredSymbolInfo.cs index 766b832ec21d6..a1b74c03656ba 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/DeclaredSymbolInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/DeclaredSymbolInfo.cs @@ -94,6 +94,7 @@ internal enum DeclaredSymbolInfoKind : byte public byte TypeParameterCount => GetTypeParameterCount(_flags); public bool IsNestedType => GetIsNestedType(_flags); public bool IsPartial => GetIsPartial(_flags); + public bool HasAttributes => GetHasAttributes(_flags); [Obsolete("Do not call directly. Only around for serialization. Use Create instead")] public DeclaredSymbolInfo( @@ -121,6 +122,7 @@ public static DeclaredSymbolInfo Create( string? containerDisplayName, string fullyQualifiedContainerName, bool isPartial, + bool hasAttributes, DeclaredSymbolInfoKind kind, Accessibility accessibility, TextSpan span, @@ -145,7 +147,8 @@ public static DeclaredSymbolInfo Create( ((uint)parameterCount << 9) | ((uint)typeParameterCount << 13) | ((isNestedType ? 1u : 0u) << 17) | - ((isPartial ? 1u : 0u) << 18); + ((isPartial ? 1u : 0u) << 18) | + ((hasAttributes ? 1u : 0u) << 19); #pragma warning disable CS0618 // Type or member is obsolete return new DeclaredSymbolInfo( @@ -181,6 +184,9 @@ private static bool GetIsNestedType(uint flags) private static bool GetIsPartial(uint flags) => ((flags >> 18) & 1) == 1; + private static bool GetHasAttributes(uint flags) + => ((flags >> 19) & 1) == 1; + internal void WriteTo(ObjectWriter writer) { writer.WriteString(Name); @@ -219,6 +225,7 @@ internal static DeclaredSymbolInfo ReadFrom_ThrowsOnFailure(StringTable stringTa containerDisplayName: containerDisplayName, fullyQualifiedContainerName: fullyQualifiedContainerName, isPartial: GetIsPartial(flags), + hasAttributes: GetHasAttributes(flags), kind: GetKind(flags), accessibility: GetAccessibility(flags), span: span, diff --git a/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs index 46cb72de5fce2..0501cb472240c 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs @@ -311,6 +311,7 @@ string AddNamespaceDeclaredSymbolInfos(TNameSyntax name, string fullyQualifiedCo containerDisplayName: null, fullyQualifiedContainerName, isPartial: true, + hasAttributes: false, DeclaredSymbolInfoKind.Namespace, Accessibility.Public, nameSyntax.Span, diff --git a/src/Workspaces/Core/Portable/Log/EtwLogger.cs b/src/Workspaces/Core/Portable/Log/EtwLogger.cs index b52a4395db270..0221e670d55f6 100644 --- a/src/Workspaces/Core/Portable/Log/EtwLogger.cs +++ b/src/Workspaces/Core/Portable/Log/EtwLogger.cs @@ -16,17 +16,17 @@ namespace Microsoft.CodeAnalysis.Internal.Log /// internal sealed class EtwLogger : ILogger { - private readonly Lazy> _isEnabledPredicate; + private readonly Func _isEnabledPredicate; // Due to ETW specifics, RoslynEventSource.Instance needs to be initialized during EtwLogger construction // so that we can enable the listeners synchronously before any events are logged. private readonly RoslynEventSource _source = RoslynEventSource.Instance; public EtwLogger(Func isEnabledPredicate) - => _isEnabledPredicate = new Lazy>(() => isEnabledPredicate); + => _isEnabledPredicate = isEnabledPredicate; public bool IsEnabled(FunctionId functionId) - => _source.IsEnabled() && _isEnabledPredicate.Value(functionId); + => _source.IsEnabled() && _isEnabledPredicate(functionId); public void Log(FunctionId functionId, LogMessage logMessage) => _source.Log(GetMessage(logMessage), functionId); diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 50e3e8fe7a60e..4f6e7e7cdffd2 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -26,6 +26,7 @@ + @@ -33,6 +34,7 @@ + diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 9d91665102568..481fc82e97e8e 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -29,3 +29,5 @@ 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 +*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 diff --git a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs index 1902661a1a38e..0c1af18e3e6c3 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs @@ -119,7 +119,7 @@ public static bool TryCreate( try { throw new InvalidOperationException( - $"We should always be able to resolve a symbol back on the host side:\r\n{project.Name}\r\n{SymbolKeyData}\r\n{failureReason}"); + $"We should always be able to resolve a symbol back on the host side:\r\n'{project.Name}-{project.Language}'\r\n'{SymbolKeyData}'\r\n'{failureReason}'"); } catch (Exception ex) when (FatalError.ReportAndCatch(ex)) { diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs index 33b8458a85b87..8f9c00af6fd6f 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs @@ -4,12 +4,11 @@ using System; using System.Collections.Generic; -using System.IO; -using System.IO.Pipelines; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.Host; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { @@ -80,6 +79,16 @@ public async ValueTask> TryInvokeAsync( return await connection.TryInvokeAsync(invocation, cancellationToken).ConfigureAwait(false); } + public async IAsyncEnumerable TryInvokeStreamAsync( + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + where TService : class + { + using var connection = CreateConnection(callbackTarget: null); + await foreach (var result in connection.TryInvokeStreamAsync(invocation, cancellationToken).ConfigureAwait(false)) + yield return result; + } + // no solution, callback: public async ValueTask TryInvokeAsync( @@ -124,6 +133,17 @@ public async ValueTask> TryInvokeAsync( return await connection.TryInvokeAsync(solution, invocation, cancellationToken).ConfigureAwait(false); } + public async IAsyncEnumerable TryInvokeStreamAsync( + Solution solution, + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + where TService : class + { + using var connection = CreateConnection(callbackTarget: null); + await foreach (var value in connection.TryInvokeStreamAsync(solution, invocation, cancellationToken)) + yield return value; + } + // project, no callback. /// @@ -158,6 +178,17 @@ public async ValueTask> TryInvokeAsync( return await connection.TryInvokeAsync(project, invocation, cancellationToken).ConfigureAwait(false); } + public async IAsyncEnumerable TryInvokeStreamAsync( + Project project, + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + where TService : class + { + using var connection = CreateConnection(callbackTarget: null); + await foreach (var item in connection.TryInvokeStreamAsync(project, invocation, cancellationToken).ConfigureAwait(false)) + yield return item; + } + // solution, callback: public async ValueTask TryInvokeAsync( @@ -217,5 +248,18 @@ public async ValueTask> TryInvokeAsync( using var connection = CreateConnection(callbackTarget); return await connection.TryInvokeAsync(project, invocation, cancellationToken).ConfigureAwait(false); } + + // multiple solution, no callback: + + public async ValueTask TryInvokeAsync( + Solution solution1, + Solution solution2, + Func invocation, + CancellationToken cancellationToken) + where TService : class + { + using var connection = CreateConnection(callbackTarget: null); + return await connection.TryInvokeAsync(solution1, solution2, invocation, cancellationToken).ConfigureAwait(false); + } } } diff --git a/src/Workspaces/Core/Portable/Remote/RemoteServiceConnection.cs b/src/Workspaces/Core/Portable/Remote/RemoteServiceConnection.cs index 96afe3e042be9..74a71f5768486 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteServiceConnection.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteServiceConnection.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Pipelines; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -30,6 +31,10 @@ public abstract ValueTask> TryInvokeAsync( Func> invocation, CancellationToken cancellationToken); + public abstract IAsyncEnumerable TryInvokeStreamAsync( + Func> invocation, + CancellationToken cancellationToken); + // no solution, callback public abstract ValueTask TryInvokeAsync( @@ -52,6 +57,11 @@ public abstract ValueTask> TryInvokeAsync( Func> invocation, CancellationToken cancellationToken); + public abstract IAsyncEnumerable TryInvokeStreamAsync( + Solution solution, + Func> invocation, + CancellationToken cancellationToken); + // project, no callback public abstract ValueTask TryInvokeAsync( @@ -64,6 +74,11 @@ public abstract ValueTask> TryInvokeAsync( Func> invocation, CancellationToken cancellationToken); + public abstract IAsyncEnumerable TryInvokeStreamAsync( + Project project, + Func> invocation, + CancellationToken cancellationToken); + // solution, callback public abstract ValueTask TryInvokeAsync( @@ -87,5 +102,13 @@ public abstract ValueTask> TryInvokeAsync( Project project, Func> invocation, CancellationToken cancellationToken); + + // multiple solution, no callback + + public abstract ValueTask TryInvokeAsync( + Solution solution1, + Solution solution2, + Func invocation, + CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs index fb6ea9814391c..7bebc4c5c475a 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs @@ -254,7 +254,7 @@ await conflictResolution.CurrentSolution.GetRequiredDocument(_documentIdOfRename } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -460,7 +460,7 @@ await AddDeclarationConflictsAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -638,7 +638,7 @@ private async Task CheckForConflictAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -680,7 +680,7 @@ private async Task GetRenamedSymbolInCurrentSolutionAsync(MutableConfli } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -711,7 +711,7 @@ private async Task FindDocumentsAndPossibleNameConflictsAsync() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -752,7 +752,7 @@ private async Task AddDocumentsWithPotentialConflictsAsync(IEnumerable } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -833,7 +833,7 @@ private async Task AnnotateAndRename_WorkerAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs index 6f6734c49daca..7ca65141ff900 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs @@ -320,7 +320,7 @@ private static async Task AddDeclarationConflictsAsync( // therefore the exception filter in IdentifyConflictsAsync is insufficient. // See https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=378642 - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs index dc753bf0a7312..8c54739a41db8 100644 --- a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs @@ -13,11 +13,9 @@ namespace Microsoft.CodeAnalysis.SemanticModelReuse { internal abstract class AbstractSemanticModelReuseLanguageService< TMemberDeclarationSyntax, - TBaseMethodDeclarationSyntax, TBasePropertyDeclarationSyntax, TAccessorDeclarationSyntax> : ISemanticModelReuseLanguageService where TMemberDeclarationSyntax : SyntaxNode - where TBaseMethodDeclarationSyntax : TMemberDeclarationSyntax where TBasePropertyDeclarationSyntax : TMemberDeclarationSyntax where TAccessorDeclarationSyntax : SyntaxNode { @@ -30,7 +28,7 @@ internal abstract class AbstractSemanticModelReuseLanguageService< public abstract SyntaxNode? TryGetContainingMethodBodyForSpeculation(SyntaxNode node); - protected abstract Task TryGetSpeculativeSemanticModelWorkerAsync(SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken); + protected abstract SemanticModel? TryGetSpeculativeSemanticModelWorker(SemanticModel previousSemanticModel, SyntaxNode previousBodyNode, SyntaxNode currentBodyNode); protected abstract SyntaxList GetAccessors(TBasePropertyDeclarationSyntax baseProperty); protected abstract TBasePropertyDeclarationSyntax GetBasePropertyDeclaration(TAccessorDeclarationSyntax accessor); @@ -41,7 +39,7 @@ internal abstract class AbstractSemanticModelReuseLanguageService< // This operation is only valid if top-level equivalent trees were passed in. If they're not equivalent // then something very bad happened as we did that document.Project.GetDependentSemanticVersionAsync was - // still the same. So somehow w don't have top-level equivalence, but we do have the same semantic version. + // still the same. So somehow we don't have top-level equivalence, but we do have the same semantic version. // // log a NFW to help diagnose what the source looks like as it may help us determine what sort of edit is // causing this. @@ -67,8 +65,28 @@ internal abstract class AbstractSemanticModelReuseLanguageService< return null; } - return await TryGetSpeculativeSemanticModelWorkerAsync( - previousSemanticModel, currentBodyNode, cancellationToken).ConfigureAwait(false); + var previousRoot = await previousSemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var currentRoot = await currentBodyNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var previousBodyNode = GetPreviousBodyNode(previousRoot, currentRoot, currentBodyNode); + + // Trivia is ignore when comparing two trees for quivalence at top level, since it has no effect to API shape + // and it'd be safe to drop in the new method body as long as the shape doesn't change. However, trivia changes + // around the method do make it tricky to decide whether a position is safe for speculation. + + // class C { void M() { return; } }"; + // ^ this is the position used to set OriginalPositionForSpeculation when creating the speculative model. + // + // class C { void M() { return null; } }"; + // ^ it's unsafe to use the speculative model at this position, even though it's part of the + // method body and after OriginalPositionForSpeculation. + + // Given that the common use case for us is continuously editing/typing inside a method body, we believe we can be conservative + // in creating speculative model with those kind of trivia change, by requring the method body block not to shift position, + // w/o sacrificing performance in those common sceanrios. + if (previousBodyNode.SpanStart != currentBodyNode.SpanStart) + return null; + + return TryGetSpeculativeSemanticModelWorker(previousSemanticModel, previousBodyNode, currentBodyNode); } protected SyntaxNode GetPreviousBodyNode(SyntaxNode previousRoot, SyntaxNode currentRoot, SyntaxNode currentBodyNode) 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/Serialization/SerializerService_Reference.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs index 5da50a09ed3ee..b0d6f3a62c449 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs @@ -647,7 +647,7 @@ public SerializedMetadataReference( protected override DocumentationProvider CreateDocumentationProvider() { // this uses documentation provider given at the constructor - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } protected override Metadata GetMetadataImpl() diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs index e156221421870..12f3d124d8fac 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs @@ -4,12 +4,27 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; +using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Shared.Extensions { + internal static class AsyncEnumerable + { + public static readonly IAsyncEnumerable Empty = GetEmptyAsync(); + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + private static async IAsyncEnumerable GetEmptyAsync() + { + yield break; + } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + } + internal static class IAsyncEnumerableExtensions { public static async Task> ToImmutableArrayAsync(this IAsyncEnumerable values, CancellationToken cancellationToken) @@ -20,5 +35,68 @@ public static async Task> ToImmutableArrayAsync(this IAsync return result.ToImmutable(); } + + /// + /// Takes an array of s and produces a single resultant with all their values merged together. Absolutely no ordering guarantee is + /// provided. It will be expected that the individual values from distinct enumerables will be interleaved + /// together. + /// + /// This helper is useful when doign parallel processing of work where each job returns an , but one final stream is desired as the result. + public static IAsyncEnumerable MergeAsync(this ImmutableArray> streams, CancellationToken cancellationToken) + { + // Code provided by Stephen Toub, but heavily modified after that. + + // 1024 chosen as a way to ensure we don't necessarily create a huge unbounded channel, while also making it + // so that we're unlikely to throttle on any stream unless there is truly a huge amount of results in it. + var channel = Channel.CreateBounded(1024); + + var tasks = new Task[streams.Length]; + for (var i = 0; i < streams.Length; i++) + tasks[i] = Process(streams[i], channel.Writer, cancellationToken); + + // Complete the channel writer with the result of all the tasks. If nothing failed, t.Exception will be + // null and this will complete successfully. If anything failed, the exception will propagate out. + // + // Note: passing CancellationToken.None here is intentional/correct. We must complete all the channels to + // allow reading to complete as well. + Task.WhenAll(tasks).ContinueWith( + t => channel.Writer.Complete(t.Exception), + CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); + + return ReadAllAsync(channel.Reader, cancellationToken); + + static async Task Process(IAsyncEnumerable stream, ChannelWriter writer, CancellationToken cancellationToken) + { + await foreach (var value in stream) + await writer.WriteAsync(value, cancellationToken).ConfigureAwait(false); + } + + static async IAsyncEnumerable ReadAllAsync(ChannelReader reader, [EnumeratorCancellation] CancellationToken cancellationToken) + { + while (await reader.WaitToReadAsync(cancellationToken).ConfigureAwait(false)) + { + while (reader.TryRead(out var item)) + yield return item; + } + } + } + + /// + /// Tasks an array of value producing tasks and produces a stream of results out of them. Like absolutely no ordering guarantee is provided. It will be expected that the + /// individual values from distinct tasks will be interleaved together. + /// + public static IAsyncEnumerable StreamAsync(this ImmutableArray> tasks, CancellationToken cancellationToken) + { + return tasks.SelectAsArray(static (t, cancellationToken) => CreateStream(t, cancellationToken), cancellationToken).MergeAsync(cancellationToken); + + static async IAsyncEnumerable CreateStream( + Task task, [EnumeratorCancellation] CancellationToken cancellationToken) + { + yield return await task.ConfigureAwait(false); + } + } } } 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/Shared/Extensions/SyntaxGeneratorExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs index c69892ee4c25f..18426217f50d5 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs @@ -489,7 +489,7 @@ public static async Task OverrideAsync( } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs index a56212aff28fe..715bd7bb8e6e1 100644 --- a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs +++ b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs @@ -6,13 +6,14 @@ namespace Microsoft.CodeAnalysis.Shared.TestHooks { internal static class FeatureAttribute { + public const string AddImportsOnPaste = nameof(AddImportsOnPaste); public const string AutomaticEndConstructCorrection = nameof(AutomaticEndConstructCorrection); public const string AutomaticPairCompletion = nameof(AutomaticPairCompletion); public const string BraceHighlighting = nameof(BraceHighlighting); public const string CallHierarchy = nameof(CallHierarchy); public const string Classification = nameof(Classification); - public const string CodeModel = nameof(CodeModel); public const string CodeDefinitionWindow = nameof(CodeDefinitionWindow); + public const string CodeModel = nameof(CodeModel); public const string CompletionSet = nameof(CompletionSet); public const string DesignerAttributes = nameof(DesignerAttributes); public const string DiagnosticService = nameof(DiagnosticService); @@ -29,12 +30,13 @@ internal static class FeatureAttribute public const string GoToImplementation = nameof(GoToImplementation); public const string GraphProvider = nameof(GraphProvider); public const string InfoBar = nameof(InfoBar); - public const string InlineDiagnostics = nameof(InlineDiagnostics); public const string InheritanceMargin = nameof(InheritanceMargin); + public const string InlineDiagnostics = nameof(InlineDiagnostics); public const string InlineHints = nameof(InlineHints); public const string InlineRenameFlyout = nameof(InlineRenameFlyout); public const string InteractiveEvaluator = nameof(InteractiveEvaluator); public const string KeywordHighlighting = nameof(KeywordHighlighting); + public const string LanguageServer = nameof(LanguageServer); public const string LibraryManager = nameof(LibraryManager); public const string LightBulb = nameof(LightBulb); public const string LineSeparators = nameof(LineSeparators); @@ -48,18 +50,17 @@ internal static class FeatureAttribute public const string ReferenceHighlighting = nameof(ReferenceHighlighting); public const string Rename = nameof(Rename); public const string RenameTracking = nameof(RenameTracking); - public const string SolutionChecksumUpdater = nameof(SolutionChecksumUpdater); - public const string SourceGenerators = nameof(SourceGenerators); public const string RuleSetEditor = nameof(RuleSetEditor); public const string SignatureHelp = nameof(SignatureHelp); public const string Snippets = nameof(Snippets); - public const string SolutionCrawler = nameof(SolutionCrawler); + public const string SolutionChecksumUpdater = nameof(SolutionChecksumUpdater); + public const string SolutionCrawlerLegacy = nameof(SolutionCrawlerLegacy); + public const string SolutionCrawlerUnitTesting = nameof(SolutionCrawlerUnitTesting); + public const string SourceGenerators = nameof(SourceGenerators); public const string StringIndentation = nameof(StringIndentation); public const string TaskList = nameof(TaskList); public const string Telemetry = nameof(Telemetry); - public const string LanguageServer = nameof(LanguageServer); public const string ValueTracking = nameof(ValueTracking); public const string Workspace = nameof(Workspace); - public const string AddImportsOnPaste = nameof(AddImportsOnPaste); } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs b/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs index ae3ce1a453a93..51a30be4bab02 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs @@ -248,7 +248,7 @@ void AddItemsToBatch(IEnumerable items) // have a problem that needs addressing. // // Not this code is unreachable because ReportAndPropagateUnlessCanceled returns false along all codepaths. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs b/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs index 0ed4993cd8366..34c6c553b4dbc 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs @@ -209,13 +209,13 @@ private static int EncodeAndAdvance(string src, int srcIndex, char[] dest, ref i public override int Read() { // XmlReader does not call this API - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override int Peek() { // XmlReader does not call this API - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs b/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs index 7843757f0aaca..9bfd6168b6b75 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons.cs @@ -2,44 +2,39 @@ // 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.Collections; using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.SolutionCrawler { + [DataContract] internal partial struct InvocationReasons : IEnumerable { public static readonly InvocationReasons Empty = new(ImmutableHashSet.Empty); + [DataMember(Order = 0)] private readonly ImmutableHashSet _reasons; public InvocationReasons(string reason) - : this(ImmutableHashSet.Create(reason)) + : this(ImmutableHashSet.Create(reason)) { } - private InvocationReasons(ImmutableHashSet reasons) - => _reasons = reasons; + public InvocationReasons(ImmutableHashSet reasons) + => _reasons = reasons ?? ImmutableHashSet.Empty; + + public bool IsEmpty => _reasons.IsEmpty; public bool Contains(string reason) => _reasons.Contains(reason); public InvocationReasons With(InvocationReasons invocationReasons) - => new((_reasons ?? ImmutableHashSet.Empty).Union(invocationReasons._reasons)); + => new(_reasons.Union(invocationReasons._reasons)); public InvocationReasons With(string reason) - => new((_reasons ?? ImmutableHashSet.Empty).Add(reason)); - - public bool IsEmpty - { - get - { - return _reasons == null || _reasons.Count == 0; - } - } + => new(_reasons.Add(reason)); public ImmutableHashSet.Enumerator GetEnumerator() => _reasons.GetEnumerator(); diff --git a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs index 11687f1346ed0..3de84caecda15 100644 --- a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Text; using Microsoft.CodeAnalysis.Host; @@ -11,6 +12,6 @@ namespace Microsoft.CodeAnalysis.SourceGeneratorTelemetry { internal interface ISourceGeneratorTelemetryCollectorWorkspaceService : IWorkspaceService { - void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo); + void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo, ProjectState project); } } diff --git a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs index 48549d468bf09..1092811e2d1ee 100644 --- a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs @@ -3,8 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.Utilities; @@ -14,15 +17,20 @@ internal class SourceGeneratorTelemetryCollectorWorkspaceService : ISourceGenera { private sealed record GeneratorTelemetryKey { - public GeneratorTelemetryKey(ISourceGenerator generator) + [SetsRequiredMembers] + public GeneratorTelemetryKey(ISourceGenerator generator, AnalyzerReference analyzerReference) { - Identity = new SourceGeneratorIdentity(generator); - FileVersion = FileVersionInfo.GetVersionInfo(generator.GetGeneratorType().Assembly.Location).FileVersion ?? "(null)"; + Identity = new SourceGeneratorIdentity(generator, analyzerReference); + FileVersion = "(null)"; + + if (Identity.AssemblyPath != null) + { + FileVersion = FileVersionInfo.GetVersionInfo(Identity.AssemblyPath).FileVersion; + } } - // TODO: mark these 'required' when we have the attributes in place - public SourceGeneratorIdentity Identity { get; } - public string FileVersion { get; } + public required SourceGeneratorIdentity Identity { get; init; } + public required string FileVersion { get; init; } } /// @@ -34,19 +42,19 @@ public GeneratorTelemetryKey(ISourceGenerator generator) private readonly StatisticLogAggregator _elapsedTimeByGenerator = new(); private readonly StatisticLogAggregator _producedFilesByGenerator = new(); - private GeneratorTelemetryKey GetTelemetryKey(ISourceGenerator generator) - => _generatorTelemetryKeys.GetValue(generator, static g => new GeneratorTelemetryKey(g)); + private GeneratorTelemetryKey GetTelemetryKey(ISourceGenerator generator, ProjectState project) + => _generatorTelemetryKeys.GetValue(generator, g => new GeneratorTelemetryKey(g, project.GetAnalyzerReferenceForGenerator(g))); - public void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo) + public void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo, ProjectState project) { foreach (var generatorTime in driverTimingInfo.GeneratorTimes) { - _elapsedTimeByGenerator.AddDataPoint(GetTelemetryKey(generatorTime.Generator), generatorTime.ElapsedTime); + _elapsedTimeByGenerator.AddDataPoint(GetTelemetryKey(generatorTime.Generator, project), generatorTime.ElapsedTime); } foreach (var generatorResult in driverRunResult.Results) { - _producedFilesByGenerator.AddDataPoint(GetTelemetryKey(generatorResult.Generator), generatorResult.GeneratedSources.Length); + _producedFilesByGenerator.AddDataPoint(GetTelemetryKey(generatorResult.Generator, project), generatorResult.GeneratedSources.Length); } } diff --git a/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs b/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs index f0ba483265687..69ee4e874a597 100644 --- a/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs +++ b/src/Workspaces/Core/Portable/Storage/AbstractPersistentStorageService.cs @@ -193,7 +193,7 @@ public static IChecksummedPersistentStorage AddReferenceCountToAndCreateWrapper( { // This should only be called from a caller that has a non-null storage that it // already has a reference on. So .TryAddReference cannot fail. - return new PersistentStorageReferenceCountedDisposableWrapper(storage.TryAddReference() ?? throw ExceptionUtilities.Unreachable); + return new PersistentStorageReferenceCountedDisposableWrapper(storage.TryAddReference() ?? throw ExceptionUtilities.Unreachable()); } public void Dispose() diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/Interop/Result.cs b/src/Workspaces/Core/Portable/Storage/SQLite/Interop/Result.cs index 3e09236ab30cc..14880af7143f6 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/Interop/Result.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/Interop/Result.cs @@ -14,15 +14,15 @@ internal enum Result // INTERNAL = 2, /* Internal logic error in SQLite */ // PERM = 3, /* Access permission denied */ // ABORT = 4, /* Callback routine requested an abort */ - BUSY = 5, /* The database file is locked */ - LOCKED = 6, /* A table in the database is locked */ - NOMEM = 7, /* A malloc() failed */ + // BUSY = 5, /* The database file is locked */ + // LOCKED = 6, /* A table in the database is locked */ + // NOMEM = 7, /* A malloc() failed */ // READONLY = 8, /* Attempt to write a readonly database */ // INTERRUPT = 9, /* Operation terminated by sqlite3_interrupt()*/ - IOERR = 10, /* Some kind of disk I/O error occurred */ + // IOERR = 10, /* Some kind of disk I/O error occurred */ // CORRUPT = 11, /* The database disk image is malformed */ // NOTFOUND = 12, /* Unknown opcode in sqlite3_file_control() */ - FULL = 13, /* Insertion failed because database is full */ + // FULL = 13, /* Insertion failed because database is full */ // CANTOPEN = 14, /* Unable to open the database file */ // PROTOCOL = 15, /* Database lock protocol error */ // EMPTY = 16, /* Database is empty */ diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlConnection.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlConnection.cs index a5c287fff5bfa..f52a20807cc6f 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlConnection.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlConnection.cs @@ -206,18 +206,26 @@ public ResettableSqlStatement GetResettableStatement(string query) return new ResettableSqlStatement(statement); } - public void RunInTransaction(Action action, TState state) + /// + public SqlException? RunInTransaction(Action action, TState state, bool throwOnSqlException) { - RunInTransaction( + var (_, exception) = RunInTransaction( static state => { state.action(state.state); return (object?)null; }, - (action, state)); + (action, state), + throwOnSqlException); + + return exception; } - public TResult RunInTransaction(Func action, TState state) + /// If a that happens during excution of should bubble out of this method or not. If , then the exception + /// will be returned in the result value instead + public (TResult? result, SqlException? exception) RunInTransaction( + Func action, TState state, bool throwOnSqlException) { try { @@ -231,34 +239,39 @@ public TResult RunInTransaction(Func action, T ExecuteCommand("begin transaction"); var result = action(state); ExecuteCommand("commit transaction"); - return result; + return (result, null); } - catch (SqlException ex) when (ex.Result is Result.FULL or - Result.IOERR or - Result.BUSY or - Result.LOCKED or - Result.NOMEM) + catch (SqlException ex) { // See documentation here: https://sqlite.org/lang_transaction.html - // If certain kinds of errors occur within a transaction, the transaction - // may or may not be rolled back automatically. The errors that can cause - // an automatic rollback include: - - // SQLITE_FULL: database or disk full - // SQLITE_IOERR: disk I/ O error - // SQLITE_BUSY: database in use by another process - // SQLITE_LOCKED: database in use by another connection in the same process - // SQLITE_NOMEM: out or memory - - // It is recommended that applications respond to the errors listed above by - // explicitly issuing a ROLLBACK command. If the transaction has already been - // rolled back automatically by the error response, then the ROLLBACK command - // will fail with an error, but no harm is caused by this. + // + // If certain kinds of errors occur within a transaction, the transaction may or may not be rolled back + // automatically. + // + // ... + // + // It is recommended that applications respond to the errors listed above by explicitly issuing a + // ROLLBACK command. If the transaction has already been rolled back automatically by the error + // response, then the ROLLBACK command will fail with an error, but no harm is caused by this. + // + // End of sqlite documentation. + + // Because of the above, we know we may be in an incomplete state, so we always do a rollback to get us + // back to a clean state. We ignore errors here as it's know that this can fail, but will cause no + // harm. Rollback(throwOnError: false); - throw; + + if (throwOnSqlException) + throw; + + return (default, ex); } catch (Exception) { + // Some other exception occurred outside of sqlite entirely (like a null-ref exception in our own code). + // Rollback (throwing if that rollback failed for some reason), then continue the exception higher up + // to tear down the callers as well. + Rollback(throwOnError: true); throw; } diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs index 21ce2fbda36f1..c012eca753301 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs @@ -59,7 +59,7 @@ public Result Step(bool throwOnError = true) if (throwOnError) { _connection.Throw(stepResult); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLiteConnectionPoolService.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLiteConnectionPoolService.cs index 9b8b9f8084646..852f14d941e4c 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLiteConnectionPoolService.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLiteConnectionPoolService.cs @@ -84,7 +84,7 @@ public void Dispose() { if (_connectionPools.TryGetValue(databaseFilePath, out var pool)) { - return pool.TryAddReference() ?? throw ExceptionUtilities.Unreachable; + return pool.TryAddReference() ?? throw ExceptionUtilities.Unreachable(); } // try to get db ownership lock. if someone else already has the lock. it will throw @@ -103,7 +103,7 @@ public void Dispose() // Place the initial ownership reference in _connectionPools, and return another _connectionPools.Add(databaseFilePath, pool); - return pool.TryAddReference() ?? throw ExceptionUtilities.Unreachable; + return pool.TryAddReference() ?? throw ExceptionUtilities.Unreachable(); } catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, cancellationToken)) { diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.Accessor.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.Accessor.cs index 0340e11fd8f5f..b721e7a4fefa6 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.Accessor.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.Accessor.cs @@ -232,7 +232,7 @@ private Optional ReadDataBlob( // passed in, we need to validate that the checksums match. This is // only safe if we are in a transaction and no-one else can race with // us. - return connection.RunInTransaction( + var (stream, exception) = connection.RunInTransaction( static t => { // If we were passed a checksum, make sure it matches what we have @@ -246,7 +246,13 @@ private Optional ReadDataBlob( return t.connection.ReadDataBlob_MustRunInTransaction(t.database, t.self.Table, t.rowId); }, - (self: this, connection, database, checksum, rowId)); + (self: this, connection, database, checksum, rowId), + throwOnSqlException: true); + + // we should never have gotten a SqlException while reading since we passed throwOnSqlException: true above. + Contract.ThrowIfTrue(exception != null); + + return stream; } private Optional ReadChecksum( @@ -255,9 +261,15 @@ private Optional ReadDataBlob( // Have to run the checksum reading in a transaction. This is necessary as blob reading outside a // transaction is not safe to do with the sqlite API. It may produce corrupt bits if another thread is // writing to the blob. - return connection.RunInTransaction( + var (stream, exception) = connection.RunInTransaction( static t => t.connection.ReadChecksum_MustRunInTransaction(t.database, t.self.Table, t.rowId), - (self: this, connection, database, rowId)); + (self: this, connection, database, rowId), + throwOnSqlException: true); + + // we should never have gotten a SqlException while reading since we passed throwOnSqlException: true above. + Contract.ThrowIfTrue(exception != null); + + return stream; } private bool ChecksumsMatch_MustRunInTransaction(SqlConnection connection, Database database, long rowId, Checksum checksum) diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs index aa24f8c4271f7..5a2faf38e08c4 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs @@ -71,12 +71,22 @@ private void FlushInMemoryDataToDisk() using var _ = _connectionPool.Target.GetPooledConnection(out var connection); - connection.RunInTransaction(static state => + var exception = connection.RunInTransaction(static state => { state.self._solutionAccessor.FlushInMemoryDataToDisk_MustRunInTransaction(state.connection); state.self._projectAccessor.FlushInMemoryDataToDisk_MustRunInTransaction(state.connection); state.self._documentAccessor.FlushInMemoryDataToDisk_MustRunInTransaction(state.connection); - }, (self: this, connection)); + }, + (self: this, connection), + throwOnSqlException: false); + + if (exception != null) + { + // Some sql exception occurred (like SQLITE_FULL). These are not exceptions we can suitably recover + // from. In this case, transition the storage instance into being unusable. Future reads/writes will + // get empty results. + this.DisableStorage(); + } } } } diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs index 5c880d765846b..addf8e5f8ee51 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs @@ -72,19 +72,29 @@ internal partial class SQLitePersistentStorage // values. try { - stringId = connection.RunInTransaction( + // Pass in `throwOnSqlException: false` so we get the exception bubbled back to us as a result value. + var (result, exception) = connection.RunInTransaction( static t => t.self.InsertStringIntoDatabase_MustRunInTransaction(t.connection, t.value), - (self: this, connection, value)); + (self: this, connection, value), + throwOnSqlException: false); - Contract.ThrowIfTrue(stringId == null); - return stringId; - } - catch (SqlException ex) when (ex.Result == Result.CONSTRAINT) - { - // We got a constraint violation. This means someone else beat us to adding this - // string to the string-table. We should always be able to find the string now. - stringId = TryGetStringIdFromDatabaseWorker(connection, value, canReturnNull: false); - return stringId; + if (exception != null) + { + // we can get two types of exceptions. A 'CONSTRAINT' violation is an expected result and means + // someone else beat us to adding this string to the string-table. As such, we should always be + // able to find the string now. + if (exception.Result == Result.CONSTRAINT) + return TryGetStringIdFromDatabaseWorker(connection, value, canReturnNull: false); + + // Some other sql exception occurred (like SQLITE_FULL). These are not exceptions we can suitably + // recover from. In this case, transition the storage instance into being unusable. Future + // reads/writes will get empty results. + this.DisableStorage(); + return null; + } + + // If we didn't get an exception, we must have gotten a string back. + return result; } catch (Exception ex) { 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/Utilities/SpellChecker.cs b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs index 9923b13c7eefc..3db88e1e4ec40 100644 --- a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs +++ b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs @@ -27,7 +27,7 @@ public SpellChecker(Checksum checksum, BKTree bKTree) _bkTree = bKTree; } - public SpellChecker(Checksum checksum, IEnumerable> corpus) + public SpellChecker(Checksum checksum, IEnumerable corpus) : this(checksum, BKTree.Create(corpus)) { } 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 97d533a846920..66f0e205b31fc 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) @@ -123,11 +125,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 FileTextLoader(absolutePath, commandLineArguments.Encoding), + loader: new WorkspaceFileTextLoader(tmpWorkspace.Services.SolutionServices, absolutePath, commandLineArguments.Encoding), filePath: absolutePath); docs.Add(doc); @@ -154,7 +156,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); @@ -170,11 +172,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 +188,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/IWorkpacePartialSolutionsTestHook.cs b/src/Workspaces/Core/Portable/Workspace/Host/IWorkpacePartialSolutionsTestHook.cs new file mode 100644 index 0000000000000..9246a3c841725 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Host/IWorkpacePartialSolutionsTestHook.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.CodeAnalysis.Host; + +internal interface IWorkpacePartialSolutionsTestHook : IWorkspaceService +{ + bool IsPartialSolutionDisabled { get; set; } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Host/IWorkspaceTestLogger.cs b/src/Workspaces/Core/Portable/Workspace/Host/IWorkspaceTestLogger.cs new file mode 100644 index 0000000000000..2eebc9b33f7bd --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Host/IWorkspaceTestLogger.cs @@ -0,0 +1,14 @@ +// 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; + +namespace Microsoft.CodeAnalysis.Host; + +internal interface IWorkspaceTestLogger : IWorkspaceService +{ + public void Log(string message); +} diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs index fb97fdbab4e69..81757f7cc1bc4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/AbstractPersistentStorage.cs @@ -19,6 +19,8 @@ internal abstract class AbstractPersistentStorage : IChecksummedPersistentStorag public string DatabaseFile { get; } public string DatabaseDirectory => Path.GetDirectoryName(DatabaseFile) ?? throw ExceptionUtilities.UnexpectedValue(DatabaseFile); + private bool _isDisabled; + protected AbstractPersistentStorage( string workingFolderPath, string solutionFilePath, @@ -34,6 +36,12 @@ protected AbstractPersistentStorage( } } + private bool IsDisabled + => Volatile.Read(ref _isDisabled); + + protected void DisableStorage() + => Volatile.Write(ref _isDisabled, true); + public abstract void Dispose(); public abstract ValueTask DisposeAsync(); @@ -49,57 +57,57 @@ protected AbstractPersistentStorage( protected abstract Task WriteStreamAsync(DocumentKey documentKey, Document? document, string name, Stream stream, Checksum? checksum, CancellationToken cancellationToken); public Task ChecksumMatchesAsync(ProjectKey projectKey, string name, Checksum checksum, CancellationToken cancellationToken) - => ChecksumMatchesAsync(projectKey, project: null, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : ChecksumMatchesAsync(projectKey, project: null, name, checksum, cancellationToken); public Task ChecksumMatchesAsync(DocumentKey documentKey, string name, Checksum checksum, CancellationToken cancellationToken) - => ChecksumMatchesAsync(documentKey, document: null, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : ChecksumMatchesAsync(documentKey, document: null, name, checksum, cancellationToken); public Task ReadStreamAsync(ProjectKey projectKey, string name, Checksum? checksum, CancellationToken cancellationToken) - => ReadStreamAsync(projectKey, project: null, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(projectKey, project: null, name, checksum, cancellationToken); public Task ReadStreamAsync(DocumentKey documentKey, string name, Checksum? checksum, CancellationToken cancellationToken) - => ReadStreamAsync(documentKey, document: null, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(documentKey, document: null, name, checksum, cancellationToken); public Task WriteStreamAsync(ProjectKey projectKey, string name, Stream stream, Checksum? checksum, CancellationToken cancellationToken) - => WriteStreamAsync(projectKey, project: null, name, stream, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(projectKey, project: null, name, stream, checksum, cancellationToken); public Task WriteStreamAsync(DocumentKey documentKey, string name, Stream stream, Checksum? checksum, CancellationToken cancellationToken) - => WriteStreamAsync(documentKey, document: null, name, stream, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(documentKey, document: null, name, stream, checksum, cancellationToken); public Task ChecksumMatchesAsync(Project project, string name, Checksum checksum, CancellationToken cancellationToken) - => ChecksumMatchesAsync(ProjectKey.ToProjectKey(project), project, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : ChecksumMatchesAsync(ProjectKey.ToProjectKey(project), project, name, checksum, cancellationToken); public Task ChecksumMatchesAsync(Document document, string name, Checksum checksum, CancellationToken cancellationToken) - => ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), document, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), document, name, checksum, cancellationToken); public Task ReadStreamAsync(Project project, string name, Checksum? checksum, CancellationToken cancellationToken) - => ReadStreamAsync(ProjectKey.ToProjectKey(project), project, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(ProjectKey.ToProjectKey(project), project, name, checksum, cancellationToken); public Task ReadStreamAsync(Document document, string name, Checksum? checksum, CancellationToken cancellationToken) - => ReadStreamAsync(DocumentKey.ToDocumentKey(document), document, name, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(DocumentKey.ToDocumentKey(document), document, name, checksum, cancellationToken); public Task ReadStreamAsync(string name, CancellationToken cancellationToken) - => ReadStreamAsync(name, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(name, checksum: null, cancellationToken); public Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken) - => ReadStreamAsync(project, name, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(project, name, checksum: null, cancellationToken); public Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken) - => ReadStreamAsync(document, name, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.Null() : ReadStreamAsync(document, name, checksum: null, cancellationToken); public Task WriteStreamAsync(Project project, string name, Stream stream, Checksum? checksum, CancellationToken cancellationToken) - => WriteStreamAsync(ProjectKey.ToProjectKey(project), project, name, stream, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(ProjectKey.ToProjectKey(project), project, name, stream, checksum, cancellationToken); public Task WriteStreamAsync(Document document, string name, Stream stream, Checksum? checksum, CancellationToken cancellationToken) - => WriteStreamAsync(DocumentKey.ToDocumentKey(document), document, name, stream, checksum, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(DocumentKey.ToDocumentKey(document), document, name, stream, checksum, cancellationToken); public Task WriteStreamAsync(string name, Stream stream, CancellationToken cancellationToken) - => WriteStreamAsync(name, stream, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(name, stream, checksum: null, cancellationToken); public Task WriteStreamAsync(Project project, string name, Stream stream, CancellationToken cancellationToken) - => WriteStreamAsync(project, name, stream, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(project, name, stream, checksum: null, cancellationToken); public Task WriteStreamAsync(Document document, string name, Stream stream, CancellationToken cancellationToken) - => WriteStreamAsync(document, name, stream, checksum: null, cancellationToken); + => IsDisabled ? SpecializedTasks.False : WriteStreamAsync(document, name, stream, checksum: null, cancellationToken); } } 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 57ec57afdb00a..841c49238fe12 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,8 @@ internal readonly struct SyntaxTreeInfo { public readonly string FilePath; public readonly ParseOptions Options; - public readonly ValueSource TextSource; + public readonly ITextAndVersionSource TextSource; + public readonly LoadTextOptions LoadTextOptions; public readonly Encoding Encoding; public readonly int Length; public readonly bool ContainsDirectives; @@ -27,7 +28,8 @@ internal readonly struct SyntaxTreeInfo public SyntaxTreeInfo( string filePath, ParseOptions options, - ValueSource textSource, + ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, Encoding encoding, int length, bool containsDirectives) @@ -35,6 +37,7 @@ public SyntaxTreeInfo( FilePath = filePath ?? string.Empty; Options = options; TextSource = textSource; + LoadTextOptions = loadTextOptions; Encoding = encoding; Length = length; ContainsDirectives = containsDirectives; @@ -42,7 +45,7 @@ public SyntaxTreeInfo( internal bool TryGetText([NotNullWhen(true)] out SourceText? text) { - if (TextSource.TryGetValue(out var textAndVersion)) + if (TextSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { text = textAndVersion.Text; return true; @@ -52,9 +55,12 @@ internal bool TryGetText([NotNullWhen(true)] out SourceText? text) return false; } + internal SourceText GetText(CancellationToken cancellationToken) + => TextSource.GetValue(LoadTextOptions, cancellationToken).Text; + internal async Task GetTextAsync(CancellationToken cancellationToken) { - var textAndVersion = await TextSource.GetValueAsync(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); @@ -154,7 +163,7 @@ protected override async Task RecoverAsync(CancellationToken cancellation { Contract.ThrowIfNull(_storage); - using (RoslynEventSource.LogInformationalBlock(FunctionId.Workspace_Recoverable_RecoverRootAsync, _containingTree.FilePath, cancellationToken)) + using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverRootAsync, _containingTree.FilePath, cancellationToken, LogLevel.Information)) { using var stream = await _storage.ReadStreamAsync(cancellationToken).ConfigureAwait(false); return RecoverRoot(stream, cancellationToken); @@ -165,7 +174,7 @@ protected override TRoot Recover(CancellationToken cancellationToken) { Contract.ThrowIfNull(_storage); - using (RoslynEventSource.LogInformationalBlock(FunctionId.Workspace_Recoverable_RecoverRoot, _containingTree.FilePath, cancellationToken)) + using (Logger.LogBlock(FunctionId.Workspace_Recoverable_RecoverRoot, _containingTree.FilePath, cancellationToken, LogLevel.Information)) { using var stream = _storage.ReadStream(cancellationToken); return RecoverRoot(stream, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs index 34ff948601901..0398744853e7b 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, LoadTextOptions loadTextOptions, Encoding encoding, SyntaxNode root); public abstract SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); public virtual bool CanCreateRecoverableTree(SyntaxNode root) @@ -68,7 +68,7 @@ protected static SyntaxNode RecoverNode(SyntaxTree tree, TextSpan textSpan, int } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs index d4e0f83d04d95..5792dee379bb9 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, LoadTextOptions loadTextOptions, 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 6f555c5abd674..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 = default); + 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 = default); + 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 ae3a638b03f00..ad36c04be9a20 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,23 +22,19 @@ public TextFactoryService() { } - public SourceText CreateText(Stream stream, Encoding? defaultEncoding, CancellationToken cancellationToken = default) + 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 = default) + 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/Host/WorkpacePartialSolutionsTestHook.cs b/src/Workspaces/Core/Portable/Workspace/Host/WorkpacePartialSolutionsTestHook.cs new file mode 100644 index 0000000000000..84623fc3651f7 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Host/WorkpacePartialSolutionsTestHook.cs @@ -0,0 +1,22 @@ +// 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 Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.UnitTests; + +[ExportWorkspaceService(typeof(IWorkpacePartialSolutionsTestHook), ServiceLayer.Host), Shared] +internal class WorkpacePartialSolutionsTestHook : IWorkpacePartialSolutionsTestHook +{ + public bool IsPartialSolutionDisabled { get; set; } + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public WorkpacePartialSolutionsTestHook() + { + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs index d2584f640cded..1707f2b3cda6a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AdditionalDocumentState.cs @@ -17,17 +17,18 @@ private AdditionalDocumentState( HostWorkspaceServices solutionServices, IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, - SourceText? sourceText, - ValueSource textAndVersionSource) - : base(solutionServices, documentServiceProvider, attributes, sourceText, textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) + : base(solutionServices, documentServiceProvider, attributes, textAndVersionSource, loadTextOptions) { _additionalText = new AdditionalTextWithState(this); } public AdditionalDocumentState( DocumentInfo documentInfo, + LoadTextOptions loadTextOptions, HostWorkspaceServices solutionServices) - : base(documentInfo, solutionServices) + : base(documentInfo, loadTextOptions, solutionServices) { _additionalText = new AdditionalTextWithState(this); } @@ -43,14 +44,14 @@ 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, 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 df2bdcd5dc8a0..9d1347935b69b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs @@ -20,17 +20,18 @@ private AnalyzerConfigDocumentState( HostWorkspaceServices solutionServices, IDocumentServiceProvider documentServiceProvider, DocumentInfo.DocumentAttributes attributes, - SourceText sourceTextOpt, - ValueSource textAndVersionSource) - : base(solutionServices, documentServiceProvider, attributes, sourceTextOpt, textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) + : base(solutionServices, documentServiceProvider, attributes, textAndVersionSource, loadTextOptions) { _analyzerConfigValueSource = CreateAnalyzerConfigValueSource(); } public AnalyzerConfigDocumentState( DocumentInfo documentInfo, + LoadTextOptions loadTextOptions, HostWorkspaceServices solutionServices) - : base(documentInfo, solutionServices) + : base(documentInfo, loadTextOptions, solutionServices) { _analyzerConfigValueSource = CreateAnalyzerConfigValueSource(); } @@ -55,14 +56,14 @@ 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, 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 new file mode 100644 index 0000000000000..69c21e66ee47b --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -0,0 +1,40 @@ +// 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; +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 bool CanReloadText + => false; + + 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; + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index cdbe35e0c7ad8..61b709e5ccce6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -314,7 +314,7 @@ public bool TryGetSemanticModel([NotNullWhen(returnValue: true)] out SemanticMod } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -418,7 +418,7 @@ public async Task> GetTextChangesAsync(Document oldDocum } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 51b8b71b3c7ef..de436542b7c4a 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 @@ -71,6 +72,9 @@ internal DocumentInfo(DocumentAttributes attributes, TextLoader? loader, IDocume DocumentServiceProvider = documentServiceProvider; } + /// + /// Creates info. + /// public static DocumentInfo Create( DocumentId id, string name, @@ -80,32 +84,19 @@ 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, + 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, - filePath, - isGenerated, - designTimeOnly: false, 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, @@ -143,6 +134,12 @@ public DocumentInfo WithFilePath(string? filePath) public DocumentInfo WithTextLoader(TextLoader? loader) => With(loader: loader); + 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); @@ -239,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) @@ -247,7 +250,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 +262,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 6894c089338ec..8568024f30407 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; @@ -38,10 +39,10 @@ protected DocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, ParseOptions? options, - SourceText? sourceText, - ValueSource textSource, + ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, ValueSource? treeSource) - : base(solutionServices, documentServiceProvider, attributes, sourceText, textSource) + : base(solutionServices, documentServiceProvider, attributes, textSource, loadTextOptions) { Contract.ThrowIfFalse(_options is null == _treeSource is null); @@ -53,9 +54,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; @@ -71,15 +73,17 @@ public DocumentState( { Contract.ThrowIfNull(options); _treeSource = CreateLazyFullyParsedTree( - base.TextAndVersionSource, + TextAndVersionSource, + LoadTextOptions, info.Id.ProjectId, - GetSyntaxTreeFilePath(info.Attributes), + info.Attributes.SyntaxTreeFilePath, options, languageServices); } } [MemberNotNullWhen(true, nameof(_treeSource))] + [MemberNotNullWhen(true, nameof(_options))] internal bool SupportsSyntaxTree => _treeSource != null; @@ -95,23 +99,9 @@ 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( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -119,13 +109,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( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -135,7 +126,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 @@ -151,7 +142,8 @@ private static async Task FullyParseTreeAsync( } private static TreeAndVersion FullyParseTree( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, + LoadTextOptions loadTextOptions, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -161,7 +153,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 @@ -177,7 +169,7 @@ private static TreeAndVersion FullyParseTree( } private static TreeAndVersion CreateTreeAndVersion( - ValueSource newTextSource, + ITextAndVersionSource newTextSource, ProjectId cacheKey, string? filePath, ParseOptions options, @@ -195,36 +187,38 @@ 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); 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); } private static ValueSource CreateLazyIncrementallyParsedTree( ValueSource oldTreeSource, - ValueSource 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, - ValueSource newTextSource, + 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); @@ -232,20 +226,21 @@ private static async Task IncrementallyParseTreeAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } private static TreeAndVersion IncrementallyParseTree( ValueSource oldTreeSource, - ValueSource newTextSource, + 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); @@ -253,7 +248,7 @@ private static TreeAndVersion IncrementallyParseTree( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -277,7 +272,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); } private const int MaxTextChangeRangeLength = 1024 * 4; @@ -326,6 +321,42 @@ public bool HasContentChanged(DocumentState oldState) public bool HasTextChanged(DocumentState oldState) => HasTextChanged(oldState, ignoreUnchangeableDocument: false); + public DocumentState UpdateChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + { + var newLoadTextOptions = new LoadTextOptions(checksumAlgorithm); + + 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, + Attributes.SyntaxTreeFilePath, + _options, + _languageServices) : null; + + return new DocumentState( + LanguageServices, + LanguageServices.WorkspaceServices, + Services, + Attributes, + _options, + TextAndVersionSource, + newLoadTextOptions, + newTreeSource); + } + public DocumentState UpdateParseOptions(ParseOptions options, bool onlyPreprocessorDirectiveChange) { var originalSourceKind = this.SourceCodeKind; @@ -359,6 +390,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,29 +402,30 @@ 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(Attributes.SyntaxTreeFilePath, options, existingTree.Encoding, LoadTextOptions.ChecksumAlgorithm, existingRoot); } if (newTree is not null) - newTreeSource = new ConstantValueSource(TreeAndVersion.Create(newTree, existingTreeAndVersion.Version)); + newTreeSource = new ConstantValueSource(new TreeAndVersion(newTree, existingTreeAndVersion.Version)); } // If we weren't able to reuse in a smart way, just reparse newTreeSource ??= CreateLazyFullyParsedTree( - TextAndVersionSource, - Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), - options, - _languageServices); + TextAndVersionSource, + LoadTextOptions, + Id.ProjectId, + Attributes.SyntaxTreeFilePath, + options, + _languageServices); return new DocumentState( LanguageServices, - solutionServices, + LanguageServices.WorkspaceServices, Services, Attributes.With(sourceCodeKind: options.Kind), options, - sourceText, TextAndVersionSource, + LoadTextOptions, newTreeSource); } @@ -420,12 +453,12 @@ private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes attribute return new DocumentState( _languageServices, - solutionServices, + LanguageServices.WorkspaceServices, Services, attributes, _options, - sourceText, TextAndVersionSource, + LoadTextOptions, _treeSource); } @@ -439,9 +472,10 @@ public DocumentState UpdateFilePath(string? filePath) var newTreeSource = SupportsSyntaxTree ? CreateLazyFullyParsedTree( TextAndVersionSource, + LoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(newAttributes), - _options!, + newAttributes.SyntaxTreeFilePath, + _options, _languageServices) : null; return new DocumentState( @@ -450,8 +484,8 @@ public DocumentState UpdateFilePath(string? filePath) Services, newAttributes, _options, - sourceText, TextAndVersionSource, + LoadTextOptions, newTreeSource); } @@ -461,25 +495,29 @@ public DocumentState UpdateFilePath(string? filePath) public new DocumentState UpdateText(TextAndVersion newTextAndVersion, PreservationMode mode) => (DocumentState)base.UpdateText(newTextAndVersion, mode); - protected override TextDocumentState UpdateText(ValueSource newTextSource, PreservationMode mode, bool incremental) + public new DocumentState UpdateText(TextLoader loader, PreservationMode mode) + => (DocumentState)base.UpdateText(loader, mode); + + protected override TextDocumentState UpdateText(ITextAndVersionSource newTextSource, PreservationMode mode, bool incremental) { ValueSource? newTreeSource; - if (_treeSource == null) + if (!SupportsSyntaxTree) { newTreeSource = null; } else if (incremental) { - newTreeSource = CreateLazyIncrementallyParsedTree(_treeSource, newTextSource); + newTreeSource = CreateLazyIncrementallyParsedTree(_treeSource, newTextSource, LoadTextOptions); } else { newTreeSource = CreateLazyFullyParsedTree( newTextSource, + LoadTextOptions, Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), - _options!, + 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? } @@ -490,35 +528,11 @@ protected override TextDocumentState UpdateText(ValueSource newT Services, Attributes, _options, - sourceText: null, textSource: newTextSource, + LoadTextOptions, treeSource: newTreeSource); } - internal DocumentState UpdateText(TextLoader loader, SourceText? text, PreservationMode mode) - { - var documentState = (DocumentState)UpdateText(loader, mode); - - // If we are given a SourceText directly, fork it since we didn't pass that into the base. - // TODO: understand why this is being called this way at all. It seems we only have a text in a specific case - // when we are opening a file, when it seems this could have just called the other overload that took a - // TextAndVersion that could have just pinned the object directly. - if (text == null) - { - return documentState; - } - - return new DocumentState( - LanguageServices, - solutionServices, - Services, - Attributes, - _options, - sourceText: text, - textSource: documentState.TextAndVersionSource, - treeSource: documentState._treeSource); - } - internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) { if (!SupportsSyntaxTree) @@ -549,10 +563,8 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) var syntaxTreeFactory = _languageServices.GetRequiredService(); - 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, newTextVersion, newTreeVersion, encoding, LoadTextOptions.ChecksumAlgorithm, Attributes, _options, syntaxTreeFactory, mode); return new DocumentState( LanguageServices, @@ -560,9 +572,9 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) Services, Attributes, _options, - sourceText: null, textSource: text, - treeSource: new ConstantValueSource(tree)); + LoadTextOptions, + treeSource: new ConstantValueSource(treeAndVersion)); } private VersionStamp GetNewTreeVersionForUpdatedTree(SyntaxNode newRoot, VersionStamp newTextVersion, PreservationMode mode) @@ -583,23 +595,23 @@ 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(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( @@ -607,8 +619,7 @@ private static (ValueSource, TreeAndVersion) CreateRecoverableTe tree.GetTextAsync, tree.GetText, cacheResult: true), - textVersion, - filePath); + textVersion); } else { @@ -623,16 +634,15 @@ 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); + textVersion); - tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, filePath, options, lazyTextAndVersion, encoding, newRoot); + tree = factory.CreateRecoverableTree(attributes.Id.ProjectId, attributes.SyntaxTreeFilePath, options, lazyTextAndVersion, new LoadTextOptions(checksumAlgorithm), encoding, newRoot); } - return (lazyTextAndVersion, TreeAndVersion.Create(tree, treeVersion)); + return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); } internal override Task GetLoadDiagnosticAsync(CancellationToken cancellationToken) @@ -647,7 +657,7 @@ private static (ValueSource, TreeAndVersion) CreateRecoverableTe 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 9d60f5c34a30f..16bc4f27b467c 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,36 +15,37 @@ internal partial class DocumentState /// /// A source for constructed from an syntax tree. /// - private sealed class TreeTextSource : ValueSource, ITextVersionable + private sealed class TreeTextSource : 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 bool CanReloadText + => false; + + public TreeTextSource(ValueSource textSource, VersionStamp version) { - _lazyText = text; + _textSource = textSource; _version = version; - _filePath = filePath; } - public override async Task GetValueAsync(CancellationToken cancellationToken = default) + public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) { - var text = await _lazyText.GetValueAsync(cancellationToken).ConfigureAwait(false); - return TextAndVersion.Create(text, _version, _filePath); + var text = await _textSource.GetValueAsync(cancellationToken).ConfigureAwait(false); + return TextAndVersion.Create(text, _version); } - public override TextAndVersion GetValue(CancellationToken cancellationToken = default) + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) { - var text = _lazyText.GetValue(cancellationToken); - return TextAndVersion.Create(text, _version, _filePath); + var text = _textSource.GetValue(cancellationToken); + return TextAndVersion.Create(text, _version); } - public override bool TryGetValue(out TextAndVersion value) + public bool TryGetValue(LoadTextOptions options, [NotNullWhen(true)] out TextAndVersion? value) { - if (_lazyText.TryGetValue(out var text)) + if (_textSource.TryGetValue(out var text)) { - value = TextAndVersion.Create(text, _version, _filePath); + value = TextAndVersion.Create(text, _version); return true; } else @@ -55,7 +55,7 @@ public override bool TryGetValue(out TextAndVersion value) } } - public bool TryGetTextVersion(out VersionStamp version) + public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) { version = _version; return version != default; diff --git a/src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs similarity index 85% rename from src/Workspaces/Core/Portable/Workspace/FileTextLoader.cs rename to src/Workspaces/Core/Portable/Workspace/Solution/FileTextLoader.cs index 40bd368fd8c6f..827d7084b39e1 100644 --- a/src/Workspaces/Core/Portable/Workspace/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,18 +62,41 @@ 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); - } + internal 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 . + /// + /// Stream. + /// Obsolete. Null. + protected virtual SourceText CreateText(Stream stream, Workspace? workspace) + => EncodedStringText.Create(stream, DefaultEncoding, checksumAlgorithm: SourceHashAlgorithms.Default); + + /// + /// Creates from . + /// +#pragma warning disable CS0618 // Type or member is obsolete + 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(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -150,7 +177,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, options, cancellationToken); textAndVersion = TextAndVersion.Create(text, version, Path); } @@ -169,11 +196,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(LoadTextOptions options, CancellationToken cancellationToken) { ValidateFileLength(Path); @@ -185,7 +212,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, 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 new file mode 100644 index 0000000000000..c6b8eb49fb6d8 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -0,0 +1,22 @@ +// 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 +{ + /// + /// 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/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 new file mode 100644 index 0000000000000..ba38c0915c6e8 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -0,0 +1,71 @@ +// 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 : ITextAndVersionSource +{ + private sealed class LazyValueWithOptions + { + public readonly LoadableTextAndVersionSource Source; + public readonly AsyncLazy LazyValue; + public readonly LoadTextOptions Options; + + public LazyValueWithOptions(LoadableTextAndVersionSource source, LoadTextOptions options) + { + LazyValue = new AsyncLazy(LoadAsync, LoadSynchronously, source.CacheResult); + Source = source; + Options = options; + } + + private Task LoadAsync(CancellationToken cancellationToken) + => Source.Loader.LoadTextAsync(Options, cancellationToken); + + private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) + => Source.Loader.LoadTextSynchronously(Options, cancellationToken); + } + + public readonly TextLoader Loader; + public readonly bool CacheResult; + + private LazyValueWithOptions? _lazyValue; + + public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) + { + Loader = loader; + CacheResult = cacheResult; + } + + public bool CanReloadText + => Loader.CanReloadText; + + private AsyncLazy GetLazyValue(LoadTextOptions options) + { + var lazy = _lazyValue; + + if (lazy == null || lazy.Options != options) + { + // drop previous value and replace it with the one that has current options: + _lazyValue = lazy = new LazyValueWithOptions(this, options); + } + + 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/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index d82a0b0f98dad..e4e40dd90aec4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -106,7 +106,9 @@ internal Project(Solution solution, ProjectState projectState) /// Immutable snapshot of language services from the host environment associated with this project's language. /// Use this over when possible. /// +#pragma warning disable CS0618 // Type or member is obsolete. Use Services instead. - This is the implementation of Services. public LanguageServices Services => LanguageServices.LanguageServices; +#pragma warning restore CS0618 // Type or member is obsolete. Use Services instead. - This is the implementation of Services. /// /// The language associated with the project. 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..1e0cfaf4dd896 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -61,9 +62,9 @@ internal partial class ProjectState private AnalyzerOptions? _lazyAnalyzerOptions; /// - /// Backing field for ; this is a default ImmutableArray if it hasn't been computed yet. + /// The list of source generators and the analyzer reference they came from. /// - private ImmutableArray _lazySourceGenerators; + private ImmutableDictionary? _lazySourceGenerators; private ProjectState( ProjectInfo projectInfo, @@ -103,9 +104,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); @@ -119,8 +121,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); @@ -235,9 +237,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) { @@ -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,61 @@ private ProjectState With( analyzerConfigSet ?? _lazyAnalyzerConfigOptions); } - private ProjectInfo.ProjectAttributes Attributes + internal ProjectInfo.ProjectAttributes Attributes => ProjectInfo.Attributes; - private ProjectState WithAttributes(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); + + return With(projectInfo: ProjectInfo.With(attributes: attributes)); + } public ProjectState WithName(string name) - => (name == Name) ? this : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(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 : WithAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); + => (runAnalyzers == RunAnalyzers) ? this : WithNewerAttributes(Attributes.With(runAnalyzers: runAnalyzers, version: Version.GetNewerVersion())); + + public ProjectState WithChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + { + if (checksumAlgorithm == ChecksumAlgorithm) + { + return this; + } + + return With( + projectInfo: ProjectInfo.With(attributes: Attributes.With(checksumAlgorithm: checksumAlgorithm, version: Version.GetNewerVersion())), + documentStates: UpdateDocumentsChecksumAlgorithm(checksumAlgorithm)); + } + + private TextDocumentStates UpdateDocumentsChecksumAlgorithm(SourceHashAlgorithm checksumAlgorithm) + => DocumentStates.UpdateStates(static (state, checksumAlgorithm) => state.UpdateChecksumAlgorithm(checksumAlgorithm), checksumAlgorithm); public ProjectState WithCompilationOptions(CompilationOptions options) { @@ -744,7 +772,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) @@ -796,20 +824,38 @@ public ProjectState WithAnalyzerReferences(IEnumerable analyz return With(projectInfo: ProjectInfo.WithAnalyzerReferences(analyzerReferences).WithVersion(Version.GetNewerVersion())); } - public ImmutableArray SourceGenerators + [MemberNotNull(nameof(_lazySourceGenerators))] + private void EnsureSourceGeneratorsInitialized() { - get + if (_lazySourceGenerators == null) { - if (_lazySourceGenerators.IsDefault) + var builder = ImmutableDictionary.CreateBuilder(); + + foreach (var analyzerReference in AnalyzerReferences) { - var generators = AnalyzerReferences.SelectMany(a => a.GetGenerators(this.Language)).ToImmutableArray(); - ImmutableInterlocked.InterlockedInitialize(ref _lazySourceGenerators, generators); + foreach (var generator in analyzerReference.GetGenerators(Language)) + builder.Add(generator, analyzerReference); } - return _lazySourceGenerators; + Interlocked.CompareExchange(ref _lazySourceGenerators, builder.ToImmutable(), comparand: null); } } + public IEnumerable SourceGenerators + { + get + { + EnsureSourceGeneratorsInitialized(); + return _lazySourceGenerators.Keys; + } + } + + public AnalyzerReference GetAnalyzerReferenceForGenerator(ISourceGenerator generator) + { + EnsureSourceGeneratorsInitialized(); + return _lazySourceGenerators[generator]; + } + public ProjectState AddDocuments(ImmutableArray documents) { Debug.Assert(!documents.Any(d => DocumentStates.Contains(d.Id))); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs index 208a70b618e7f..aefabca1a621c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs @@ -82,7 +82,7 @@ private async Task ComputeChecksumsAsync(CancellationToke } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index fded0834bc902..4eaa9d6b06bff 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -16,53 +16,70 @@ namespace Microsoft.CodeAnalysis /// /// A recoverable TextAndVersion source that saves its text to temporary storage. /// - internal sealed class RecoverableTextAndVersion : ValueSource, ITextVersionable + internal sealed class RecoverableTextAndVersion : ITextVersionable, ITextAndVersionSource { private readonly SolutionServices _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 bool CanReloadText { get; } + + public RecoverableTextAndVersion(ITextAndVersionSource initialSource, SolutionServices services) { _initialSourceOrRecoverableText = initialSource; _services = services; + CanReloadText = initialSource.CanReloadText; } - public ITemporaryTextStorageInternal? Storage - => (_initialSourceOrRecoverableText as RecoverableText)?.Storage; - - public override bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) + /// + /// 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: var sourceOrRecoverableText = _initialSourceOrRecoverableText; - if (sourceOrRecoverableText is RecoverableText recoverableText) + source = sourceOrRecoverableText as ITextAndVersionSource; + if (source != null) { - if (recoverableText.TryGetValue(out var text)) - { - value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); - return true; - } + text = null; + return true; + } + + text = (RecoverableText)sourceOrRecoverableText; + return false; + } + + public ITemporaryTextStorageInternal? Storage + => (_initialSourceOrRecoverableText as RecoverableText)?.Storage; + + public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) + { + if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) + { + return source.TryGetValue(options, out value); } - else + + if (recoverableText.TryGetValue(out var text) && recoverableText.LoadTextOptions == options) { - return ((ValueSource)sourceOrRecoverableText).TryGetValue(out value); + value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); + return true; } value = null; 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; @@ -72,33 +89,53 @@ 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 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(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 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(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; + if (recoverableText.LoadTextOptions != options && recoverableText.InitialSource != null) + { + Interlocked.Exchange( + ref _initialSourceOrRecoverableText, + new RecoverableText(recoverableText.InitialSource, await recoverableText.InitialSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false), options, _services)); + } + + recoverableText = (RecoverableText)_initialSourceOrRecoverableText; return recoverableText.ToTextAndVersion(await recoverableText.GetValueAsync(cancellationToken).ConfigureAwait(false)); } @@ -107,16 +144,27 @@ private sealed class RecoverableText : WeaklyCachedRecoverableValueSource(textAndVersion.Text)) { _storageService = services.GetRequiredService(); Version = textAndVersion.Version; LoadDiagnostic = textAndVersion.LoadDiagnostic; + LoadTextOptions = options; + + if (source.CanReloadText) + { + // reloadable source must not cache results + Contract.ThrowIfTrue(source is LoadableTextAndVersionSource { CacheResult: true }); + + InitialSource = source; + } } public TextAndVersion ToTextAndVersion(SourceText text) @@ -155,10 +203,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/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index b68de95eb67d8..ecfea4241e501 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. /// @@ -976,7 +992,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 +1012,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 info = DocumentInfo.Create( + var project = GetRequiredProjectState(documentId.ProjectId); + var sourceText = SourceText.From(string.Empty, encoding: null, project.ChecksumAlgorithm); + + 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 +1063,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 +1180,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 +1193,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. /// @@ -1585,12 +1597,7 @@ public Solution WithDocumentTextLoader(DocumentId documentId, TextLoader loader, throw new ArgumentOutOfRangeException(nameof(mode)); } - return UpdateDocumentTextLoader(documentId, loader, text: null, mode: mode); - } - - internal Solution UpdateDocumentTextLoader(DocumentId documentId, TextLoader loader, SourceText? text, PreservationMode mode) - { - var newState = _state.UpdateDocumentTextLoader(documentId, loader, text, mode); + var newState = _state.UpdateDocumentTextLoader(documentId, loader, mode); // Note: state is currently not reused. // If UpdateDocumentTextLoader is changed to reuse the state replace this assert with Solution instance reusal. 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/SolutionState.CompilationAndGeneratorDriverTranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationAndGeneratorDriverTranslationAction_Actions.cs index 2679b177db7bb..636d8676cdaa1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationAndGeneratorDriverTranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationAndGeneratorDriverTranslationAction_Actions.cs @@ -330,7 +330,7 @@ public ReplaceGeneratorDriverAction(GeneratorDriver oldGeneratorDriver, ProjectS var generatorDriver = _oldGeneratorDriver.ReplaceAdditionalTexts(_newProjectState.AdditionalDocumentStates.SelectAsArray(static documentState => documentState.AdditionalText)) .WithUpdatedParseOptions(_newProjectState.ParseOptions!) .WithUpdatedAnalyzerConfigOptions(_newProjectState.AnalyzerOptions.AnalyzerConfigOptionsProvider) - .ReplaceGenerators(_newProjectState.SourceGenerators); + .ReplaceGenerators(_newProjectState.SourceGenerators.ToImmutableArray()); return generatorDriver; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs index 71d30f9cb155e..14ac1af3fb969 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs @@ -387,11 +387,7 @@ public Task GetCompilationAsync(SolutionState solution, Cancellatio { if (this.TryGetCompilation(out var compilation)) { - // PERF: This is a hot code path and Task isn't cheap, - // so cache the completed tasks to reduce allocations. We also - // need to avoid keeping a strong reference to the Compilation, - // so use a ConditionalWeakTable. - return SpecializedTasks.FromResult(compilation); + return Task.FromResult(compilation); } else if (cancellationToken.IsCancellationRequested) { @@ -453,7 +449,7 @@ private async Task GetOrBuildDeclarationCompilationAsync(SolutionSe } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -496,7 +492,7 @@ private async Task GetOrBuildCompilationInfoAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -571,7 +567,7 @@ private async Task BuildCompilationInfoFromScratchAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -601,7 +597,7 @@ private async Task BuildDeclarationCompilationFromScratchAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -639,7 +635,7 @@ private async Task BuildFinalStateFromInProgressStateAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -704,7 +700,7 @@ private async Task BuildFinalStateFromInProgressStateAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -834,7 +830,7 @@ private async Task FinalizeCompilationAsync( generatorInfo = generatorInfo.WithDriver(compilationFactory.CreateGeneratorDriver( this.ProjectState.ParseOptions!, - ProjectState.SourceGenerators, + ProjectState.SourceGenerators.ToImmutableArray(), this.ProjectState.AnalyzerOptions.AnalyzerConfigOptionsProvider, additionalTexts)); } @@ -880,7 +876,7 @@ private async Task FinalizeCompilationAsync( generatorInfo = generatorInfo.WithDriver(generatorInfo.Driver!.RunGenerators(compilationToRunGeneratorsOn, cancellationToken)); - solution.Services.GetService()?.CollectRunResult(generatorInfo.Driver!.GetRunResult(), generatorInfo.Driver!.GetTimingInfo()); + solution.Services.GetService()?.CollectRunResult(generatorInfo.Driver!.GetRunResult(), generatorInfo.Driver!.GetTimingInfo(), ProjectState); var runResult = generatorInfo.Driver!.GetRunResult(); @@ -907,11 +903,14 @@ private async Task FinalizeCompilationAsync( continue; } + var generatorAnalyzerReference = this.ProjectState.GetAnalyzerReferenceForGenerator(generatorResult.Generator); + foreach (var generatedSource in generatorResult.GeneratedSources) { var existing = FindExistingGeneratedDocumentState( generatorInfo.Documents, generatorResult.Generator, + generatorAnalyzerReference, generatedSource.HintName); if (existing != null) @@ -933,14 +932,15 @@ private async Task FinalizeCompilationAsync( ProjectState.Id, generatedSource.HintName, generatorResult.Generator, - generatedSource.SyntaxTree.FilePath); + generatedSource.SyntaxTree.FilePath, + generatorAnalyzerReference); generatedDocumentsBuilder.Add( SourceGeneratedDocumentState.Create( 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 @@ -995,16 +995,17 @@ private async Task FinalizeCompilationAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } // Local functions static SourceGeneratedDocumentState? FindExistingGeneratedDocumentState( TextDocumentStates states, ISourceGenerator generator, + AnalyzerReference analyzerReference, string hintName) { - var generatorIdentity = new SourceGeneratorIdentity(generator); + var generatorIdentity = new SourceGeneratorIdentity(generator, analyzerReference); foreach (var (_, state) in states.States) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs index b18aed267bab3..248dccfc94b2d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs @@ -69,7 +69,7 @@ public ICompilationTracker Fork(ProjectState newProject, CompilationAndGenerator public ICompilationTracker FreezePartialStateWithTree(SolutionState solution, DocumentState docState, SyntaxTree tree, CancellationToken cancellationToken) { // Because we override SourceGeneratedDocument.WithFrozenPartialSemantics directly, we shouldn't be able to get here. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public async Task GetCompilationAsync(SolutionState solution, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 1f4bc17f0e1bc..b68cefc6a7ddc 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, new CompilationAndGeneratorDriverTranslationAction.ReplaceAllSyntaxTreesAction(newProject, isParseOptionChange: false)); + } + /// /// Creates a new solution instance with the project specified updated to have the name. /// @@ -1046,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))); } @@ -1105,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))); } @@ -1113,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); @@ -1373,13 +1389,13 @@ public SolutionState WithDocumentSourceCodeKind(DocumentId documentId, SourceCod return UpdateDocumentState(oldDocument.UpdateSourceCodeKind(sourceCodeKind), textChanged: true); } - public SolutionState UpdateDocumentTextLoader(DocumentId documentId, TextLoader loader, SourceText? text, PreservationMode mode) + public SolutionState UpdateDocumentTextLoader(DocumentId documentId, TextLoader loader, PreservationMode mode) { var oldDocument = GetRequiredDocumentState(documentId); // Assumes that text has changed. User could have closed a doc without saving and we are loading text from closed file with // old content. Also this should make sure we don't re-use latest doc version with data associated with opened document. - return UpdateDocumentState(oldDocument.UpdateText(loader, text, mode), textChanged: true, recalculateDependentVersions: true); + return UpdateDocumentState(oldDocument.UpdateText(loader, mode), textChanged: true, recalculateDependentVersions: true); } /// @@ -1610,7 +1626,7 @@ public SolutionState WithFrozenPartialCompilationIncludingSpecificDocument(Docum using (this.StateLock.DisposableWait(cancellationToken)) { // in progress solutions are disabled for some testing - if (this.Workspace is Workspace ws && ws.TestHookPartialSolutionsDisabled) + if (Services.GetService()?.IsPartialSolutionDisabled == true) { return this; } @@ -1652,7 +1668,7 @@ public SolutionState WithFrozenPartialCompilationIncludingSpecificDocument(Docum } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1886,7 +1902,7 @@ public SolutionState WithCachedSourceGeneratorState(ProjectId projectToUpdate, P } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -1920,7 +1936,7 @@ public SolutionState WithCachedSourceGeneratorState(ProjectId projectToUpdate, P } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs index f1f45fe6bd669..7e6cc70fc35ff 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs @@ -168,7 +168,7 @@ private async Task ComputeChecksumsAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs index 0841301656383..4c2f9baef98ec 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -21,23 +22,28 @@ internal readonly record struct SourceGeneratedDocumentIdentity { public bool ShouldReuseInSerialization => true; - public static SourceGeneratedDocumentIdentity Generate(ProjectId projectId, string hintName, ISourceGenerator generator, string filePath) + public static SourceGeneratedDocumentIdentity Generate(ProjectId projectId, string hintName, ISourceGenerator generator, string filePath, AnalyzerReference analyzerReference) { // We want the DocumentId generated for a generated output to be stable between Compilations; this is so features that track // a document by DocumentId can find it after some change has happened that requires generators to run again. // To achieve this we'll just do a crytographic hash of the generator name and hint name; the choice of a cryptographic hash // as opposed to a more generic string hash is we actually want to ensure we don't have collisions. - var generatorIdentity = new SourceGeneratorIdentity(generator); + var generatorIdentity = new SourceGeneratorIdentity(generator, analyzerReference); // Combine the strings together; we'll use Encoding.Unicode since that'll match the underlying format; this can be made much // faster once we're on .NET Core since we could directly treat the strings as ReadOnlySpan. var projectIdBytes = projectId.Id.ToByteArray(); - using var _ = ArrayBuilder.GetInstance(capacity: (generatorIdentity.AssemblyName.Length + 1 + generatorIdentity.TypeName.Length + 1 + hintName.Length) * 2 + projectIdBytes.Length, out var hashInput); + + // The assembly path should exist in any normal scenario; the hashing of the name only would apply if the user loaded a + // dynamic assembly they produced at runtime and passed us that via a custom AnalyzerReference. + var assemblyNameToHash = generatorIdentity.AssemblyPath ?? generatorIdentity.AssemblyName; + + using var _ = ArrayBuilder.GetInstance(capacity: (assemblyNameToHash.Length + 1 + generatorIdentity.TypeName.Length + 1 + hintName.Length) * 2 + projectIdBytes.Length, out var hashInput); hashInput.AddRange(projectIdBytes); // Add a null to separate the generator name and hint name; since this is effectively a joining of UTF-16 bytes // we'll use a UTF-16 null just to make sure there's absolutely no risk of collision. - hashInput.AddRange(Encoding.Unicode.GetBytes(generatorIdentity.AssemblyName)); + hashInput.AddRange(Encoding.Unicode.GetBytes(assemblyNameToHash)); hashInput.AddRange(0, 0); hashInput.AddRange(Encoding.Unicode.GetBytes(generatorIdentity.TypeName)); hashInput.AddRange(0, 0); @@ -61,6 +67,7 @@ public void WriteTo(ObjectWriter writer) writer.WriteString(HintName); writer.WriteString(Generator.AssemblyName); + writer.WriteString(Generator.AssemblyPath); writer.WriteString(Generator.AssemblyVersion.ToString()); writer.WriteString(Generator.TypeName); writer.WriteString(FilePath); @@ -72,6 +79,7 @@ internal static SourceGeneratedDocumentIdentity ReadFrom(ObjectReader reader) var hintName = reader.ReadString(); var generatorAssemblyName = reader.ReadString(); + var generatorAssemblyPath = reader.ReadString(); var generatorAssemblyVersion = Version.Parse(reader.ReadString()); var generatorTypeName = reader.ReadString(); var filePath = reader.ReadString(); @@ -79,7 +87,13 @@ internal static SourceGeneratedDocumentIdentity ReadFrom(ObjectReader reader) return new SourceGeneratedDocumentIdentity( documentId, hintName, - new SourceGeneratorIdentity(generatorAssemblyName, generatorAssemblyVersion, generatorTypeName), + new SourceGeneratorIdentity + { + AssemblyName = generatorAssemblyName, + AssemblyPath = generatorAssemblyPath, + AssemblyVersion = generatorAssemblyVersion, + TypeName = generatorTypeName + }, filePath); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 64b5a064e4feb..78d3e5105dc8a 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 ConstantValueSource(textAndVersion); + 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); } @@ -55,9 +58,10 @@ private SourceGeneratedDocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, ParseOptions options, - ValueSource textSource, + ITextAndVersionSource textSource, + LoadTextOptions loadTextOptions, ValueSource treeSource) - : base(languageServices, solutionServices, documentServiceProvider, attributes, options, sourceText: null, textSource, treeSource) + : base(languageServices, solutionServices, documentServiceProvider, attributes, options, textSource, loadTextOptions, treeSource) { Identity = documentIdentity; } @@ -65,10 +69,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/SourceGeneratorIdentity.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs index f347a0c91ef34..3f3c903417b0d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs @@ -3,21 +3,36 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Text; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis { - internal record struct SourceGeneratorIdentity(string AssemblyName, Version AssemblyVersion, string TypeName) + /// + /// Assembly path is used as a part of a generator identity to deal with the case that the user accidentally installed the same + /// generator twice from two different paths, or actually has two different generators that just happened to use the same name. + /// In the wild we've seen cases where a user has a broken project or build that results in the same generator being added twice; + /// we aren't going to try to deduplicate those anywhere since currently the compiler does't do any deduplication either: + /// you'll simply get duplicate outputs which might collide and cause compile errors. If https://github.com/dotnet/roslyn/issues/56619 + /// is addressed, we can potentially match the compiler behavior by taking a different approach here. + /// + internal record struct SourceGeneratorIdentity { - public SourceGeneratorIdentity(ISourceGenerator generator) - : this(GetGeneratorAssemblyName(generator), generator.GetGeneratorType().Assembly.GetName().Version!, GetGeneratorTypeName(generator)) - { - } + public required string AssemblyName { get; init; } + public required string? AssemblyPath { get; init; } + public required Version AssemblyVersion { get; init; } + public required string TypeName { get; init; } - public static string GetGeneratorAssemblyName(ISourceGenerator generator) + [SetsRequiredMembers] + public SourceGeneratorIdentity(ISourceGenerator generator, AnalyzerReference analyzerReference) { - return generator.GetGeneratorType().Assembly.GetName().Name!; + var generatorType = generator.GetGeneratorType(); + var assembly = generatorType.Assembly; + var assemblyName = assembly.GetName(); + AssemblyName = assemblyName.Name!; + AssemblyPath = analyzerReference.FullPath; + AssemblyVersion = assemblyName.Version!; + TypeName = generatorType.FullName!; } public static string GetGeneratorTypeName(ISourceGenerator generator) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index ae98692975601..e9c6cf1d766b1 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; @@ -18,22 +19,8 @@ internal partial class TextDocumentState { protected readonly HostWorkspaceServices solutionServices; - /// - /// A direct reference to our source text. This is only kept around in specialized scenarios. - /// Specifically, we keep this around when a document is opened. By providing this we can allow - /// clients to easily get to the text of the document in a non-blocking fashion if that's all - /// that they need. - /// - /// Note: this facility does not extend to getting the version as well. That's because the - /// version of a document depends on both the current source contents and the contents from - /// the previous version of the document. (i.e. if the contents are the same, then we will - /// preserve the same version, otherwise we'll move the version forward). Because determining - /// the version depends on comparing text, and because getting the old text may block, we - /// do not have the ability to know the version of the document up front, and instead can - /// only retrieve is asynchronously through . - /// - protected readonly SourceText? sourceText; - protected ValueSource TextAndVersionSource { get; } + internal ITextAndVersionSource TextAndVersionSource { get; } + public readonly LoadTextOptions LoadTextOptions; // Checksums for this solution state private readonly ValueSource _lazyChecksums; @@ -49,12 +36,13 @@ protected TextDocumentState( HostWorkspaceServices solutionServices, IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, - SourceText? sourceText, - ValueSource textAndVersionSource) + ITextAndVersionSource textAndVersionSource, + LoadTextOptions loadTextOptions) { this.solutionServices = solutionServices; - this.sourceText = sourceText; - this.TextAndVersionSource = textAndVersionSource; + + this.LoadTextOptions = loadTextOptions; + TextAndVersionSource = textAndVersionSource; Attributes = attributes; Services = documentServiceProvider ?? DefaultTextDocumentServiceProvider.Instance; @@ -67,15 +55,14 @@ 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, info.Id, services) - : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, Encoding.UTF8), VersionStamp.Default, info.FilePath))) + ? CreateRecoverableText(info.TextLoader, services.SolutionServices) + : CreateStrongText(TextAndVersion.Create(SourceText.From(string.Empty, encoding: null, loadTextOptions.ChecksumAlgorithm), VersionStamp.Default, info.FilePath)), + loadTextOptions) { } @@ -84,20 +71,15 @@ public TextDocumentState(DocumentInfo info, HostWorkspaceServices services) 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, DocumentId documentId, HostWorkspaceServices services) - { - return new AsyncLazy( - asynchronousComputeFunction: cancellationToken => loader.LoadTextAsync(services.Workspace, documentId, cancellationToken), - synchronousComputeFunction: cancellationToken => loader.LoadTextSynchronously(services.Workspace, documentId, cancellationToken), - cacheResult: true); - } + private static ITextAndVersionSource CreateStrongText(TextLoader loader) + => new LoadableTextAndVersionSource(loader, cacheResult: true); - protected static ValueSource CreateRecoverableText(TextAndVersion text, SolutionServices services) + private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, LoadTextOptions loadTextOptions, SolutionServices 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 @@ -105,33 +87,20 @@ protected static ValueSource CreateRecoverableText(TextAndVersio // 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; } - 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); - } + private static ITextAndVersionSource CreateRecoverableText(TextLoader loader, SolutionServices services) + => new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, cacheResult: false), services); public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; public bool TryGetText([NotNullWhen(returnValue: true)] out SourceText? text) { - if (this.sourceText != null) - { - text = sourceText; - return true; - } - - if (this.TextAndVersionSource.TryGetValue(out var textAndVersion)) + if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) { text = textAndVersion.Text; return true; @@ -148,10 +117,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; @@ -164,15 +133,10 @@ 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) { - if (sourceText != null) - { - return new ValueTask(sourceText); - } - if (TryGetText(out var text)) { return new ValueTask(text); @@ -187,13 +151,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; } @@ -213,7 +177,7 @@ public TextDocumentState UpdateText(TextAndVersion newTextAndVersion, Preservati { var newTextSource = mode == PreservationMode.PreserveIdentity ? CreateStrongText(newTextAndVersion) - : CreateRecoverableText(newTextAndVersion, this.solutionServices.SolutionServices); + : CreateRecoverableText(newTextAndVersion, LoadTextOptions, solutionServices.SolutionServices); return UpdateText(newTextSource, mode, incremental: true); } @@ -230,31 +194,31 @@ 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); } - 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, - 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)); } } @@ -263,7 +227,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(); } @@ -273,7 +237,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/TextDocumentState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs index b7b5659d3eaa2..a33b34ef52cea 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs @@ -47,7 +47,7 @@ private async Task ComputeChecksumsAsync(CancellationTok } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index 7e33d6f3f30c2..4818cdd199b29 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -73,7 +73,7 @@ public bool TryGetState(DocumentId documentId, [NotNullWhen(true)] out TState? s => _map.TryGetValue(documentId, out var state) ? state : null; public TState GetRequiredState(DocumentId documentId) - => _map.TryGetValue(documentId, out var state) ? state : throw ExceptionUtilities.Unreachable; + => _map.TryGetValue(documentId, out var state) ? state : throw ExceptionUtilities.Unreachable(); /// /// s in the order in which they were added to the project (the compilation order). diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs index b1895bb604f10..14d06653146b5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextLoader.cs @@ -3,7 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Drawing; using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -12,24 +18,62 @@ namespace Microsoft.CodeAnalysis { + internal readonly record struct LoadTextOptions(SourceHashAlgorithm ChecksumAlgorithm); + /// /// A class that represents access to a source text and its version from a storage location. /// public abstract class TextLoader { + private static readonly ConditionalWeakTable> s_isObsoleteLoadTextAndVersionAsyncOverriden = new(); + private const double MaxDelaySecs = 1.0; private const int MaxRetries = 5; internal static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(MaxDelaySecs / MaxRetries); internal virtual string? FilePath => null; + /// + /// True if reloads from its original binary representation (e.g. file on disk). + /// + internal virtual bool CanReloadText + => false; + + /// + /// Load a text and a version of the document. + /// + /// + /// Use when creating from an original binary representation. + /// Ignore if the is not created from a binary representation. + /// + /// Cancellation token. + /// + /// + /// + internal virtual Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (s_isObsoleteLoadTextAndVersionAsyncOverriden.GetValue( + GetType(), + _ => 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 {nameof(LoadTextAndVersionAsync)}"); + } + /// /// Load a text and a version of the document. /// + /// Obsolete. Null. + /// Obsolete. Null. /// /// /// - public abstract Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken); + public virtual Task LoadTextAndVersionAsync(Workspace? workspace, DocumentId? documentId, CancellationToken cancellationToken) + => LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), cancellationToken); /// /// Load a text and a version of the document in the workspace. @@ -37,13 +81,13 @@ public abstract class TextLoader /// /// /// - internal virtual TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, 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(workspace, documentId, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + return LoadTextAndVersionAsync(options, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); } - internal async Task LoadTextAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal async Task LoadTextAsync(LoadTextOptions options, CancellationToken cancellationToken) { var retries = 0; @@ -51,20 +95,20 @@ internal async Task LoadTextAsync(Workspace workspace, DocumentI { try { - return await LoadTextAndVersionAsync(workspace, documentId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); + return await LoadTextAndVersionAsync(options, 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 +116,7 @@ internal async Task LoadTextAsync(Workspace workspace, DocumentI } } - internal TextAndVersion LoadTextSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal TextAndVersion LoadTextSynchronously(LoadTextOptions options, CancellationToken cancellationToken) { var retries = 0; @@ -80,32 +124,31 @@ internal TextAndVersion LoadTextSynchronously(Workspace workspace, DocumentId do { try { - return LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken); + return LoadTextAndVersionSynchronously(options, 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 +157,7 @@ private TextAndVersion CreateFailedText(Workspace workspace, DocumentId document if (filePath == null) { location = Location.None; - display = documentId.ToString(); + display = ""; } else { @@ -164,10 +207,10 @@ private sealed class TextDocumentLoader : TextLoader internal TextDocumentLoader(TextAndVersion textAndVersion) => _textAndVersion = textAndVersion; - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) => Task.FromResult(_textAndVersion); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, CancellationToken cancellationToken) => _textAndVersion; } @@ -184,10 +227,13 @@ internal TextContainerLoader(SourceTextContainer container, VersionStamp version _filePath = filePath; } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(LoadTextAndVersionSynchronously(workspace, documentId, cancellationToken)); + internal override string? FilePath + => _filePath; + + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(LoadTextAndVersionSynchronously(options, cancellationToken)); - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + internal override TextAndVersion LoadTextAndVersionSynchronously(LoadTextOptions options, 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..fc6b7e6928be0 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,10 @@ internal sealed class TreeAndVersion /// public VersionStamp Version { get; } - private TreeAndVersion(SyntaxTree tree, VersionStamp version) + public TreeAndVersion(SyntaxTree tree, VersionStamp version) { - this.Tree = tree; - this.Version = version; - } - - public static TreeAndVersion Create(SyntaxTree tree, VersionStamp version) - { - if (tree == null) - { - throw new ArgumentNullException(nameof(tree)); - } - - return new TreeAndVersion(tree, version); + Tree = tree; + Version = version; } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 1bd916ca86e99..58bdee9dc2762 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -52,8 +52,6 @@ public abstract partial class Workspace : IDisposable // test hooks. internal static bool TestHookStandaloneProjectsDoNotHoldReferences = false; - internal bool TestHookPartialSolutionsDisabled { get; set; } - /// /// Determines whether changes made to unchangeable documents will be silently ignored or cause exceptions to be thrown /// when they are applied to workspace via . @@ -61,8 +59,6 @@ public abstract partial class Workspace : IDisposable /// internal virtual bool IgnoreUnchangeableDocumentsWhenApplyingChanges { get; } = false; - private Action? _testMessageLogger; - /// /// Constructs a new workspace instance. /// @@ -91,14 +87,7 @@ protected Workspace(HostServices host, string? workspaceKind) } internal void LogTestMessage(Func messageFactory, TArg state) - => _testMessageLogger?.Invoke(messageFactory(state)); - - /// - /// Sets an internal logger that will receive some messages. - /// - /// An action called to write a single line to the log. - internal void SetTestLogger(Action? writeLineMessageLogger) - => _testMessageLogger = writeLineMessageLogger; + => Services.GetService()?.Log(messageFactory(state)); /// /// Services provider by the host for implementing workspace features. @@ -198,7 +187,38 @@ protected Solution SetCurrentSolution(Solution solution) /// The id of the project updated by to be passed to the workspace change event. /// The id of the document updated by to be passed to the workspace change event. /// True if was set to the transformed solution, false if the transformation did not change the solution. - internal bool SetCurrentSolution(Func transformation, WorkspaceChangeKind kind, ProjectId? projectId = null, DocumentId? documentId = null) + internal bool SetCurrentSolution( + Func transformation, + WorkspaceChangeKind kind, + ProjectId? projectId = null, + DocumentId? documentId = null) + { + return SetCurrentSolution( + static (oldSolution, data) => data.transformation(oldSolution), + onAfterUpdate: static (oldSolution, newSolution, data) => + { + // Queue the event but don't execute its handlers on this thread. + // Doing so under the serialization lock guarantees the same ordering of the events + // as the order of the changes made to the solution. + data.@this.RaiseWorkspaceChangedEventAsync(data.kind, oldSolution, newSolution, data.projectId, data.documentId); + }, + (@this: this, transformation, kind, projectId, documentId)); + } + + /// + /// Applies specified transformation to , updates to the new value and raises a workspace change event of the specified kind. + /// + /// Solution transformation. + /// Action to perform once has been updated. The + /// action will be passed the old that was just replaced and the exact solution it + /// was replaced with. The latter may be different than the solution returned by as it will have its updated accordingly. + /// Updating the solution and invoking will happen atomically while is being held. + internal bool SetCurrentSolution( + Func transformation, + Action onAfterUpdate, + TData data) { Contract.ThrowIfNull(transformation); @@ -206,7 +226,7 @@ internal bool SetCurrentSolution(Func transformation, Worksp while (true) { - var transformedSolution = transformation(currentSolution); + var transformedSolution = transformation(currentSolution, data); if (transformedSolution == currentSolution) { return false; @@ -220,11 +240,7 @@ internal bool SetCurrentSolution(Func transformation, Worksp oldSolution = Interlocked.CompareExchange(ref _latestSolution, newSolution, currentSolution); if (oldSolution == currentSolution) { - // Queue the event but don't execute its handlers on this thread. - // Doing so under the serialization lock guarantees the same ordering of the events - // as the order of the changes made to the solution. - RaiseWorkspaceChangedEventAsync(kind, oldSolution, newSolution, projectId, documentId); - + onAfterUpdate(oldSolution, newSolution, data); return true; } } @@ -372,6 +388,7 @@ protected virtual void Dispose(bool finalize) } #region Host API + /// /// Call this method to respond to a solution being opened in the host environment. /// @@ -678,15 +695,9 @@ internal void OnRunAnalyzersChanged(ProjectId projectId, bool runAnalyzers) /// protected internal void OnDocumentAdded(DocumentInfo documentInfo) { - using (_serializationLock.DisposableWait()) - { - var documentId = documentInfo.Id; - - var oldSolution = this.CurrentSolution; - var newSolution = this.SetCurrentSolution(oldSolution.AddDocument(documentInfo)); - - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.DocumentAdded, oldSolution, newSolution, documentId: documentId); - } + this.SetCurrentSolution( + oldSolution => oldSolution.AddDocument(documentInfo), + WorkspaceChangeKind.DocumentAdded, documentId: documentInfo.Id); } /// @@ -694,17 +705,15 @@ protected internal void OnDocumentAdded(DocumentInfo documentInfo) /// protected internal void OnDocumentsAdded(ImmutableArray documentInfos) { - using (_serializationLock.DisposableWait()) - { - var oldSolution = this.CurrentSolution; - var newSolution = this.SetCurrentSolution(oldSolution.AddDocuments(documentInfos)); - - // Raise ProjectChanged as the event type here. DocumentAdded is presumed by many callers to have a - // DocumentId associated with it, and we don't want to be raising multiple events. - - foreach (var projectId in documentInfos.Select(i => i.Id.ProjectId).Distinct()) - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.ProjectChanged, oldSolution, newSolution, projectId); - } + this.SetCurrentSolution( + static (oldSolution, data) => oldSolution.AddDocuments(data.documentInfos), + onAfterUpdate: static (oldSolution, newSolution, data) => + { + // Raise ProjectChanged as the event type here. DocumentAdded is presumed by many callers to have a + // DocumentId associated with it, and we don't want to be raising multiple events. + foreach (var projectId in data.documentInfos.Select(i => i.Id.ProjectId).Distinct()) + data.@this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.ProjectChanged, oldSolution, newSolution, projectId); + }, (@this: this, documentInfos)); } /// @@ -712,15 +721,10 @@ protected internal void OnDocumentsAdded(ImmutableArray documentIn /// protected internal void OnDocumentReloaded(DocumentInfo newDocumentInfo) { - using (_serializationLock.DisposableWait()) - { - var documentId = newDocumentInfo.Id; - - var oldSolution = this.CurrentSolution; - var newSolution = this.SetCurrentSolution(oldSolution.RemoveDocument(documentId).AddDocument(newDocumentInfo)); - - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.DocumentReloaded, oldSolution, newSolution, documentId: documentId); - } + var documentId = newDocumentInfo.Id; + this.SetCurrentSolution( + oldSolution => oldSolution.RemoveDocument(documentId).AddDocument(newDocumentInfo), + WorkspaceChangeKind.DocumentReloaded, documentId: documentId); } /// @@ -1071,69 +1075,61 @@ protected internal void OnAnalyzerConfigDocumentRemoved(DocumentId documentId) /// protected void UpdateReferencesAfterAdd() { - using (_serializationLock.DisposableWait()) - { - var oldSolution = this.CurrentSolution; - var newSolution = UpdateReferencesAfterAdd(oldSolution); - - if (newSolution != oldSolution) - { - newSolution = this.SetCurrentSolution(newSolution); - _ = this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); - } - } - } + SetCurrentSolution( + oldSolution => UpdateReferencesAfterAdd(oldSolution), + WorkspaceChangeKind.SolutionChanged); - [System.Diagnostics.Contracts.Pure] - private static Solution UpdateReferencesAfterAdd(Solution solution) - { - // Build map from output assembly path to ProjectId - // Use explicit loop instead of ToDictionary so we don't throw if multiple projects have same output assembly path. - var outputAssemblyToProjectIdMap = new Dictionary(); - foreach (var p in solution.Projects) + [System.Diagnostics.Contracts.Pure] + static Solution UpdateReferencesAfterAdd(Solution solution) { - if (!string.IsNullOrEmpty(p.OutputFilePath)) + // Build map from output assembly path to ProjectId + // Use explicit loop instead of ToDictionary so we don't throw if multiple projects have same output assembly path. + var outputAssemblyToProjectIdMap = new Dictionary(); + foreach (var p in solution.Projects) { - outputAssemblyToProjectIdMap[p.OutputFilePath!] = p.Id; - } + if (!string.IsNullOrEmpty(p.OutputFilePath)) + { + outputAssemblyToProjectIdMap[p.OutputFilePath!] = p.Id; + } - if (!string.IsNullOrEmpty(p.OutputRefFilePath)) - { - outputAssemblyToProjectIdMap[p.OutputRefFilePath!] = p.Id; + if (!string.IsNullOrEmpty(p.OutputRefFilePath)) + { + outputAssemblyToProjectIdMap[p.OutputRefFilePath!] = p.Id; + } } - } - - // now fix each project if necessary - foreach (var pid in solution.ProjectIds) - { - var project = solution.GetProject(pid)!; - // convert metadata references to project references if the metadata reference matches some project's output assembly. - foreach (var meta in project.MetadataReferences) + // now fix each project if necessary + foreach (var pid in solution.ProjectIds) { - if (meta is PortableExecutableReference pemeta) + var project = solution.GetProject(pid)!; + + // convert metadata references to project references if the metadata reference matches some project's output assembly. + foreach (var meta in project.MetadataReferences) { - // check both Display and FilePath. FilePath points to the actually bits, but Display should match output path if - // the metadata reference is shadow copied. - if ((!RoslynString.IsNullOrEmpty(pemeta.Display) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.Display, out var matchingProjectId)) || - (!RoslynString.IsNullOrEmpty(pemeta.FilePath) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.FilePath, out matchingProjectId))) + if (meta is PortableExecutableReference pemeta) { - var newProjRef = new ProjectReference(matchingProjectId, pemeta.Properties.Aliases, pemeta.Properties.EmbedInteropTypes); - - if (!project.ProjectReferences.Contains(newProjRef)) + // check both Display and FilePath. FilePath points to the actually bits, but Display should match output path if + // the metadata reference is shadow copied. + if ((!RoslynString.IsNullOrEmpty(pemeta.Display) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.Display, out var matchingProjectId)) || + (!RoslynString.IsNullOrEmpty(pemeta.FilePath) && outputAssemblyToProjectIdMap.TryGetValue(pemeta.FilePath, out matchingProjectId))) { - project = project.AddProjectReference(newProjRef); - } + var newProjRef = new ProjectReference(matchingProjectId, pemeta.Properties.Aliases, pemeta.Properties.EmbedInteropTypes); - project = project.RemoveMetadataReference(meta); + if (!project.ProjectReferences.Contains(newProjRef)) + { + project = project.AddProjectReference(newProjRef); + } + + project = project.RemoveMetadataReference(meta); + } } } + + solution = project.Solution; } - solution = project.Solution; + return solution; } - - return solution; } #endregion @@ -1646,44 +1642,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/WorkspaceFileTextLoader.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceFileTextLoader.cs new file mode 100644 index 0000000000000..5322199a4d3ad --- /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(); + } + + 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 d9ad1487e10ba..f70dbbefe7e1c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -383,16 +383,9 @@ protected internal void OnDocumentOpened( } else { - // We don't have the old text or version. And we don't want to retrieve them - // just yet (as that would cause blocking in this synchronous method). So just - // make a simple loader to do that for us later when requested. - // + // We don't have the old text or version. Rather than trying to reuse a version that we still have, let's just assume the file has changed. // keep open document text alive by using PreserveIdentity - // - // Note: we pass along the newText here so that clients can easily get the text - // of an opened document just by calling TryGetText without any blocking. - currentSolution = oldSolution.UpdateDocumentTextLoader(documentId, - new ReuseVersionLoader((DocumentState)oldDocument.State, newText), newText, PreservationMode.PreserveIdentity); + currentSolution = oldSolution.WithDocumentText(documentId, newText, PreservationMode.PreserveValue); } var newSolution = this.SetCurrentSolution(currentSolution); @@ -462,36 +455,6 @@ internal void OnSourceGeneratedDocumentClosed(SourceGeneratedDocument document) } } - private class ReuseVersionLoader : TextLoader - { - // Capture DocumentState instead of Document so that we don't hold onto the old solution. - private readonly DocumentState _oldDocumentState; - private readonly SourceText _newText; - - public ReuseVersionLoader(DocumentState oldDocumentState, SourceText newText) - { - _oldDocumentState = oldDocumentState; - _newText = newText; - } - - public override async Task LoadTextAndVersionAsync( - Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - { - var oldText = await _oldDocumentState.GetTextAsync(cancellationToken).ConfigureAwait(false); - var version = await _oldDocumentState.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - - return GetProperTextAndVersion(oldText, _newText, version, _oldDocumentState.FilePath); - } - - internal override TextAndVersion LoadTextAndVersionSynchronously(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - { - var oldText = _oldDocumentState.GetTextSynchronously(cancellationToken); - var version = _oldDocumentState.GetTextVersionSynchronously(cancellationToken); - - return GetProperTextAndVersion(oldText, _newText, version, _oldDocumentState.FilePath); - } - } - private static TextAndVersion GetProperTextAndVersion(SourceText oldText, SourceText newText, VersionStamp version, string? filePath) { // if the supplied text is the same as the previous text, then also use same version @@ -639,7 +602,7 @@ protected internal void OnDocumentClosed(DocumentId documentId, TextLoader reloa } catch (Exception e) when (FatalError.ReportAndPropagate(e, ErrorSeverity.General)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs index 6fe88101cd26e..5c5efa17cd4b8 100644 --- a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs +++ b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using System.Text; using System.Threading; using System.Threading.Tasks; using MessagePack; @@ -31,6 +32,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.VisualBasic.CodeStyle; @@ -117,6 +119,10 @@ void AddTypeRecursive(Type type, MemberInfo declaringMember) { AddTypeRecursive(method.ReturnType.GetGenericArguments().Single(), method); } + else if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>)) + { + AddTypeRecursive(method.ReturnType.GetGenericArguments().Single(), method); + } else { // remote API must return ValueTask or ValueTask @@ -142,6 +148,54 @@ void AddTypeRecursive(Type type, MemberInfo declaringMember) return types; } + public static IEnumerable GetEncodingTestCases() + => EncodingTestHelpers.GetEncodingTestCases(); + + [Theory] + [MemberData(nameof(GetEncodingTestCases))] + public void EncodingIsMessagePackSerializable(Encoding original) + { + var messagePackOptions = MessagePackSerializerOptions.Standard.WithResolver(MessagePackFormatters.DefaultResolver); + + using var stream = new MemoryStream(); + MessagePackSerializer.Serialize(stream, original, messagePackOptions); + stream.Position = 0; + + var deserialized = (Encoding)MessagePackSerializer.Deserialize(typeof(Encoding), stream, messagePackOptions); + EncodingTestHelpers.AssertEncodingsEqual(original, deserialized); + } + + private sealed class TestEncoderFallback : EncoderFallback + { + public override int MaxCharCount => throw new NotImplementedException(); + public override EncoderFallbackBuffer CreateFallbackBuffer() => throw new NotImplementedException(); + } + + private sealed class TestDecoderFallback : DecoderFallback + { + public override int MaxCharCount => throw new NotImplementedException(); + public override DecoderFallbackBuffer CreateFallbackBuffer() => throw new NotImplementedException(); + } + + [Fact] + public void EncodingIsMessagePackSerializable_WithCustomFallbacks() + { + var messagePackOptions = MessagePackSerializerOptions.Standard.WithResolver(MessagePackFormatters.DefaultResolver); + + var original = Encoding.GetEncoding(Encoding.ASCII.CodePage, new TestEncoderFallback(), new TestDecoderFallback()); + + using var stream = new MemoryStream(); + MessagePackSerializer.Serialize(stream, original, messagePackOptions); + stream.Position = 0; + + var deserialized = (Encoding)MessagePackSerializer.Deserialize(typeof(Encoding), stream, messagePackOptions); + Assert.NotEqual(original, deserialized); + + // original throws from the custom fallback, deserialized has the default fallback: + Assert.Throws(() => original.GetBytes("\u1234")); + AssertEx.Equal(new byte[] { 0x3f }, deserialized.GetBytes("\u1234")); + } + [Fact] public void OptionsAreMessagePackSerializable_LanguageAgnostic() { diff --git a/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs b/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs index 07e2886bf26b8..4d21d74a3661d 100644 --- a/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs +++ b/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs @@ -6,9 +6,11 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery; using Roslyn.Test.Utilities; using Xunit; @@ -206,6 +208,56 @@ public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_Indexer_CSha Assert.True(model3.IsSpeculativeSemanticModel); } + [Fact] + [WorkItem(1541001, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] + [WorkItem(1587699, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] + public async Task TestOutOfBoundsInSyntaxContext1_CSharp() + { + var source = "class C { void M() { return; } }"; + var document1 = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From("class C { void M() { return null; } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + // ensure this doesn't crash. + CSharpSyntaxContext.CreateContext(document2, model2, source.IndexOf("void"), CancellationToken.None); + } + + [Fact] + [WorkItem(1541001, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] + [WorkItem(1587699, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] + public async Task TestOutOfBoundsInSyntaxContext2_CSharp() + { + // These two tree are considered equavilent at top level, but the change in trivia around the method + // makes it tricky to decide whether it's safe to use the speculative model at a given position. + + var source1 = "class C { void M() { return; } }"; + // ^ this is the position used to set OriginalPositionForSpeculation when creating the speculative model. + var source2 = "class C { void M() { return null; } }"; + // ^ it's unsafe to use the speculative model at this position, even though it's after OriginalPositionForSpeculation + + // First call will prime the cache to point at the real semantic model. + var document1 = CreateDocument(source1, LanguageNames.CSharp); + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source1.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(source2)); + + // Because the change in trivia shifted the method definition, we are not able to get a speculative model based on previous model + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source2.IndexOf("{ return"), CancellationToken.None); + Assert.False(model2.IsSpeculativeSemanticModel); + + // ensure this doesn't crash. + CSharpSyntaxContext.CreateContext(document2, model2, source2.IndexOf("{ return"), CancellationToken.None); + } + #endregion #region Visual Basic tests @@ -334,7 +386,8 @@ end function [Fact] public async Task MultipleBodyEditsShouldProduceFreshModel_VisualBasic() { - var source = @"class C + var source = @" +class C sub M() return end sub @@ -456,6 +509,71 @@ end event Assert.True(model3.IsSpeculativeSemanticModel); } + [Fact] + [WorkItem(1541001, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] + [WorkItem(1587699, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] + public async Task TestOutOfBoundsInSyntaxContext1_VisualBasic() + { + var source = @" +class C + sub M() + return + end sub +end class"; + var document1 = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(@" +class C + sub M() + return nothing + end sub +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + // Ensure this doesn't crash. + VisualBasicSyntaxContext.CreateContext(document2, model2, source.IndexOf("sub"), CancellationToken.None); + } + + [Fact] + [WorkItem(1541001, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1541001")] + [WorkItem(1587699, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1587699")] + public async Task TestOutOfBoundsInSyntaxContext2_VisualBasic() + { + var source = @" +class C + sub M() + return + end sub +end class"; + var document1 = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(@" +class C +class C + sub M() + return nothing + end sub +end class")); + + // Because the change in trivia shifted the method definition, we are not able to get a speculative model based on previous model + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model2.IsSpeculativeSemanticModel); + + // Ensure this doesn't crash. + VisualBasicSyntaxContext.CreateContext(document2, model2, source.IndexOf("return"), CancellationToken.None); + } + #endregion } } 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 572bb50a9abf4..320ce9840a42c 100644 --- a/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/DocumentInfoTests.cs @@ -8,6 +8,10 @@ using System.Collections.Immutable; 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 @@ -28,7 +32,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( @@ -45,6 +49,25 @@ public void Create() Assert.True(info.IsGenerated); } + [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] public void Create_Folders() { @@ -64,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] @@ -79,11 +106,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 FileTextLoader(Path.GetTempPath(), defaultEncoding: null)); + 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 846b69a3b252e..65221af38bbfd 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -10,8 +10,8 @@ using System.Composition; 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 +19,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,16 +26,14 @@ 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; 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.Indentation; namespace Microsoft.CodeAnalysis.UnitTests { @@ -56,9 +53,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 +233,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 +250,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 +296,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 +558,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 +581,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 +604,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 +618,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 +627,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 +635,96 @@ 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 async Task WithProjectChecksumAlgorithm_DocumentUpdates() + { + var projectId = ProjectId.CreateNewId(); + var documentAId = DocumentId.CreateNewId(projectId); + var documentBId = DocumentId.CreateNewId(projectId); + var documentCId = DocumentId.CreateNewId(projectId); + var fileDocumentId = 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(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)); + + 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); + 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); + 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 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); + } + } + [Fact] public void WithProjectName() { @@ -646,7 +740,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 +763,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 +827,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 +1269,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 +2123,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 +2231,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 +2290,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 +2357,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 +2872,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 +2885,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). @@ -2840,7 +3138,7 @@ public void TestProjectCompletenessWithMultipleProjects() Assert.True(transitivelyDependsOnNormalProjects.HasSuccessfullyLoadedAsync().Result); } - private class TestSmallFileTextLoader : FileTextLoader + private sealed class TestSmallFileTextLoader : FileTextLoader { public TestSmallFileTextLoader(string path, Encoding encoding) : base(path, encoding) @@ -2854,8 +3152,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 +3165,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(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); } catch (InvalidDataException ex) { @@ -2883,7 +3179,7 @@ public async Task TestMassiveFileSize() try { // test sync one - var unused = loader.LoadTextAndVersionSynchronously(workspace, DocumentId.CreateNewId(ProjectId.CreateNewId()), CancellationToken.None); + var unused = loader.LoadTextAndVersionSynchronously(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); } catch (InvalidDataException ex) { @@ -2905,20 +3201,24 @@ 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 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(); var recoverableTree = factory.CreateRecoverableTree( dummyProject.Id, strongTree.FilePath, strongTree.Options, - new ConstantValueSource(TextAndVersion.Create(strongTree.GetText(), VersionStamp.Create(), strongTree.FilePath)), - strongTree.GetText().Encoding, - strongTree.GetRoot()); + new ConstantTextAndVersionSource(TextAndVersion.Create(sourceText, VersionStamp.Create(), strongTree.FilePath)), + new LoadTextOptions(text.ChecksumAlgorithm), + 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..97848e3dca39f 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -91,6 +91,28 @@ public async Task WithReferencesMethodCorrectlyUpdatesRunningGenerators() Assert.Single((await project.GetRequiredCompilationAsync(CancellationToken.None)).SyntaxTrees); } + // We only run this test on Release, as the compiler has asserts that trigger in Debug that the type names probably shouldn't be the same. + [ConditionalFact(typeof(IsRelease))] + public async Task GeneratorAddedWithDifferentFilePathsProducesDistinctDocumentIds() + { + using var workspace = CreateWorkspace(); + + // Produce two generator references with different paths, but the same generator by assembly/type. We will still give them separate + // generator instances, because in the "real" analyzer reference case each analyzer reference produces it's own generator objects. + var generatorReference1 = new TestGeneratorReference(new SingleFileTestGenerator("", hintName: "DuplicateFile"), analyzerFilePath: "Z:\\A.dll"); + var generatorReference2 = new TestGeneratorReference(new SingleFileTestGenerator("", hintName: "DuplicateFile"), analyzerFilePath: "Z:\\B.dll"); + + var project = AddEmptyProject(workspace.CurrentSolution) + .AddAnalyzerReferences(new[] { generatorReference1, generatorReference2 }); + + Assert.Equal(2, (await project.GetRequiredCompilationAsync(CancellationToken.None)).SyntaxTrees.Count()); + + var generatedDocuments = (await project.GetSourceGeneratedDocumentsAsync()).ToList(); + Assert.Equal(2, generatedDocuments.Count); + + Assert.NotEqual(generatedDocuments[0].Id, generatedDocuments[1].Id); + } + [Fact] public async Task IncrementalSourceGeneratorInvokedCorrectNumberOfTimes() { @@ -770,13 +792,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/SolutionTests/TextLoaderTests.cs b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs new file mode 100644 index 0000000000000..eca670e8b2c63 --- /dev/null +++ b/src/Workspaces/CoreTest/SolutionTests/TextLoaderTests.cs @@ -0,0 +1,135 @@ +// 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); + + 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); + + 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); + + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(Value); + } + + [Theory, Obsolete] + [MemberData(nameof(GetNoOverideLoaders))] + public async Task NoOverride(TextLoader loader) + { + await Assert.ThrowsAsync(() => loader.LoadTextAndVersionAsync(new LoadTextOptions(SourceHashAlgorithms.Default), 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(new LoadTextOptions(SourceHashAlgorithms.Default), 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(new LoadTextOptions(SourceHashAlgorithms.Default), 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(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None)); + Assert.Same(LoaderOverridesNew.Value, await loader.LoadTextAndVersionAsync(workspace: null, documentId: null, CancellationToken.None)); + } +} diff --git a/src/Workspaces/CoreTest/SymbolKeyTests.cs b/src/Workspaces/CoreTest/SymbolKeyTests.cs index 2b3f5108f4f73..faa738f1b2ce5 100644 --- a/src/Workspaces/CoreTest/SymbolKeyTests.cs +++ b/src/Workspaces/CoreTest/SymbolKeyTests.cs @@ -113,6 +113,176 @@ public class B { }; TestRoundTrip(GetDeclaredSymbols(compilation), compilation); } + [Fact] + public void TestMissingField1_CSharp() + { + var source = @" + +public class C +{ + const int; +} +"; + var compilation = GetCompilation(source, LanguageNames.CSharp); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField2_CSharp() + { + var source = @" + +public class C +{ + int a,; +} +"; + var compilation = GetCompilation(source, LanguageNames.CSharp); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField3_CSharp() + { + var source = @" + +public class C +{ + const; +} +"; + var compilation = GetCompilation(source, LanguageNames.CSharp); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField1_VisualBasic() + { + var source = @" + +public class C + constant as integer +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.False(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField2_VisualBasic() + { + var source = @" + +public class C + dim a, +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField3_VisualBasic() + { + var source = @" + +public class C + dim a, as integer +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.False(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField4_VisualBasic() + { + var source = @" + +public class C + dim a as integer, +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField5_VisualBasic() + { + var source = @" + +public class C + constant +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.False(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingField6_VisualBasic() + { + var source = @" + +public class C + constant a, +end class +"; + var compilation = GetCompilation(source, LanguageNames.VisualBasic); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IFieldSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingEvent1_CSharp() + { + var source = @" + +public class C +{ + event System.Action; +} +"; + var compilation = GetCompilation(source, LanguageNames.CSharp); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IEventSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + + [Fact] + public void TestMissingEvent2_CSharp() + { + var source = @" + +public class C +{ + event System.Action a,; +} +"; + var compilation = GetCompilation(source, LanguageNames.CSharp); + var symbols = GetDeclaredSymbols(compilation); + Assert.True(symbols.Any(s => s is IEventSymbol { MetadataName: "" })); + TestRoundTrip(symbols, compilation); + } + [Fact, WorkItem(14364, "https://github.com/dotnet/roslyn/issues/14364")] public void TestVBParameterizedEvent() { @@ -1063,6 +1233,23 @@ public Goo G() { } } } + [Fact] + public void TestCrossLanguageEquality1() + { + var compilation1 = GetCompilation("", LanguageNames.CSharp); + var compilation2 = GetCompilation("", LanguageNames.VisualBasic); + + var symbolKey1 = SymbolKey.Create(compilation1.GetSpecialType(SpecialType.System_Int32)); + var symbolKey2 = SymbolKey.Create(compilation2.GetSpecialType(SpecialType.System_Int32)); + + Assert.NotEqual(symbolKey1.ToString(), symbolKey2.ToString()); + + Assert.True(symbolKey1.Equals(symbolKey2)); + Assert.True(SymbolKey.GetComparer(ignoreCase: true).Equals(symbolKey1, symbolKey2)); + Assert.True(SymbolKey.GetComparer(ignoreAssemblyKeys: true).Equals(symbolKey1, symbolKey2)); + Assert.True(SymbolKey.GetComparer(ignoreCase: true, ignoreAssemblyKeys: true).Equals(symbolKey1, symbolKey2)); + } + private static void TestRoundTrip(IEnumerable symbols, Compilation compilation, Func fnId = null) { foreach (var symbol in symbols) diff --git a/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs b/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs index 95e69447cd3f7..d961d6be49cce 100644 --- a/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs @@ -42,7 +42,7 @@ void a() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } Assert.True(false, "Should not get here because an exception should be thrown before this point."); 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/UtilityTest/SpecializedTasksTests.cs b/src/Workspaces/CoreTest/UtilityTest/SpecializedTasksTests.cs index 77f46fc2b7c6e..ea78049aa96b2 100644 --- a/src/Workspaces/CoreTest/UtilityTest/SpecializedTasksTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/SpecializedTasksTests.cs @@ -212,7 +212,7 @@ public async Task Transform_AsyncCanceledFunction_IgnoresTransform() await Task.Yield(); gate.Wait(CancellationToken.None); cts.Token.ThrowIfCancellationRequested(); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }; Func transform = (_, _) => { @@ -272,7 +272,7 @@ public async Task Transform_AsyncCanceledFunction_NotRequested_IgnoresTransform( await Task.Yield(); gate.Wait(CancellationToken.None); unexpectedCts.Token.ThrowIfCancellationRequested(); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }; Func transform = (_, _) => { @@ -339,7 +339,7 @@ public async Task Transform_AsyncCanceledFunction_MismatchToken_IgnoresTransform await Task.Yield(); gate.Wait(CancellationToken.None); unexpectedCts.Token.ThrowIfCancellationRequested(); - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); }; Func transform = (_, _) => { @@ -363,7 +363,7 @@ public void Transform_SyncDirectFaultedFunction_IgnoresTransform() { var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = new CancellationToken(canceled: false); Func> func = (_, _) => throw fault; Func transform = (_, _) => @@ -385,7 +385,7 @@ public void Transform_SyncFaultedFunction_IgnoresTransform() { var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = new CancellationToken(canceled: false); Func> func = (_, _) => new(Task.FromException(fault)); Func transform = (_, _) => @@ -407,7 +407,7 @@ public async Task Transform_AsyncFaultedFunction_IgnoresTransform() { var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = new CancellationToken(canceled: false); var gate = new ManualResetEventSlim(); Func> func = async (_, _) => @@ -440,7 +440,7 @@ public void Transform_SyncDirectFaultedFunction_CancellationRequested_IgnoresTra var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = cts.Token; Func> func = (_, _) => throw fault; Func transform = (_, _) => @@ -465,7 +465,7 @@ public void Transform_SyncFaultedFunction_CancellationRequested_IgnoresTransform var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = cts.Token; Func> func = (_, _) => new(Task.FromException(fault)); Func transform = (_, _) => @@ -490,7 +490,7 @@ public async Task Transform_AsyncFaultedFunction_CancellationRequested_IgnoresTr var executedTransform = false; - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var cancellationToken = cts.Token; var gate = new ManualResetEventSlim(); Func> func = async (_, _) => @@ -519,7 +519,7 @@ public async Task Transform_AsyncFaultedFunction_CancellationRequested_IgnoresTr [Fact] public void Transform_SyncCompletedFunction_FaultedTransform() { - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); Func> func = (_, _) => new(new IntermediateType()); Func transform = (_, _) => throw fault; var arg = new StateType(); @@ -534,7 +534,7 @@ public void Transform_SyncCompletedFunction_FaultedTransform() [Fact] public async Task Transform_AsyncCompletedFunction_FaultedTransform() { - var fault = ExceptionUtilities.Unreachable; + var fault = ExceptionUtilities.Unreachable(); var gate = new ManualResetEventSlim(); Func> func = async (_, _) => { diff --git a/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs b/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs index 4f194bdbab7bf..7c619c300a631 100644 --- a/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs @@ -396,7 +396,7 @@ private static void TestCodeStyleOptionsCommon(Workspace works { IPerLanguageValuedOption perLanguageValuedOption => new OptionKey2(perLanguageValuedOption, language!), ISingleValuedOption singleValued => new OptionKey2(singleValued), - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; // Value return from "object GetOption(OptionKey)" should always be public CodeStyleOption type. 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/MEF/ExportProviderCache.cs b/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs index b32d0ef44e796..7b59fcbab923d 100644 --- a/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs +++ b/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs @@ -264,7 +264,9 @@ public Assembly LoadAssembly(string assemblyFullName, string codeBasePath) var assemblyName = new AssemblyName(assemblyFullName); if (!string.IsNullOrEmpty(codeBasePath)) { +#pragma warning disable SYSLIB0044 assemblyName.CodeBase = codeBasePath; +#pragma warning restore SYSLIB0044 } return this.LoadAssembly(assemblyName); diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 9b84f8afe4ee2..ed243e99c208c 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -125,7 +125,7 @@ public event EventHandler? AvailabilityChanged // This method is currently not needed for our IServiceBroker usage patterns. public ValueTask GetPipeAsync(ServiceMoniker serviceMoniker, ServiceActivationOptions options, CancellationToken cancellationToken) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public ValueTask GetProxyAsync(ServiceRpcDescriptor descriptor, ServiceActivationOptions options, CancellationToken cancellationToken) where T : class { @@ -212,7 +212,9 @@ public InProcRemoteServices(SolutionServices workspaceServices, TraceListener? t RegisterRemoteBrokeredService(new RemoteInheritanceMarginService.Factory()); RegisterRemoteBrokeredService(new RemoteUnusedReferenceAnalysisService.Factory()); RegisterRemoteBrokeredService(new RemoteCompilationAvailableService.Factory()); + RegisterRemoteBrokeredService(new RemoteLegacySolutionEventsAggregationService.Factory()); RegisterRemoteBrokeredService(new RemoteStackTraceExplorerService.Factory()); + RegisterRemoteBrokeredService(new RemoteUnitTestingSearchService.Factory()); } public void Dispose() diff --git a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs index 4b633a852cd78..f80f699610c17 100644 --- a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs +++ b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs @@ -82,7 +82,7 @@ internal Thread MainThread get; } - private void MainThreadStart() => throw ExceptionUtilities.Unreachable; + private void MainThreadStart() => throw ExceptionUtilities.Unreachable(); /// /// Verifies that the current synchronization context has not been used for scheduling work. If the diff --git a/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs b/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs index fff413c2ac07c..6a7a76a3efed3 100644 --- a/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs +++ b/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs @@ -18,7 +18,7 @@ public sealed class TestGeneratorReference : AnalyzerReference, IChecksummedObje private readonly ISourceGenerator _generator; private readonly Checksum _checksum; - public TestGeneratorReference(ISourceGenerator generator) + public TestGeneratorReference(ISourceGenerator generator, string? analyzerFilePath = null) { _generator = generator; Guid = Guid.NewGuid(); @@ -31,14 +31,16 @@ public TestGeneratorReference(ISourceGenerator generator) var checksumArray = Guid.ToByteArray(); Array.Resize(ref checksumArray, Checksum.HashSize); _checksum = Checksum.From(checksumArray); + + FullPath = analyzerFilePath; } - public TestGeneratorReference(IIncrementalGenerator generator) - : this(generator.AsSourceGenerator()) + public TestGeneratorReference(IIncrementalGenerator generator, string? analyzerFilePath = null) + : this(generator.AsSourceGenerator(), analyzerFilePath) { } - public override string? FullPath => null; + public override string? FullPath { get; } public override object Id => this; public Guid Guid { get; } diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index 3b74790d6145a..7edb0a4451f27 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs @@ -13,12 +13,14 @@ namespace Roslyn.Test.Utilities { internal class TestTextLoader : TextLoader { - private readonly string _text; + private readonly TextAndVersion _textAndVersion; - public TestTextLoader(string text = "test") - => _text = text; + public TestTextLoader(string text = "test", SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithms.Default) + { + _textAndVersion = TextAndVersion.Create(SourceText.From(text, encoding: null, checksumAlgorithm), VersionStamp.Create()); + } - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(SourceText.From(_text), VersionStamp.Create())); + internal override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => Task.FromResult(_textAndVersion); } } diff --git a/src/Workspaces/CoreTestUtilities/VBOptionsFactory.cs b/src/Workspaces/CoreTestUtilities/VBOptionsFactory.cs new file mode 100644 index 0000000000000..63204ee4f5131 --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/VBOptionsFactory.cs @@ -0,0 +1,28 @@ +// 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.Linq; +using System.Reflection.Metadata; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.ExtractMethod; + +namespace Microsoft.CodeAnalysis.UnitTests; + +/// +/// Currently VB does not support required members, so it can't create instances of some of our option types. +/// This class is a workaround until VB implements the feature. +/// +internal static class VBOptionsFactory +{ + public static ExtractMethodGenerationOptions CreateExtractMethodGenerationOptions(CodeGenerationOptions codeGenerationOptions, ExtractMethodOptions extractOptions) + => new ExtractMethodGenerationOptions() + { + CodeGenerationOptions = codeGenerationOptions, + ExtractOptions = extractOptions + }; +} 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/Resources/SolutionFiles/VisualBasic.sln b/src/Workspaces/MSBuildTest/Resources/SolutionFiles/VisualBasic.sln new file mode 100644 index 0000000000000..be316ece22278 --- /dev/null +++ b/src/Workspaces/MSBuildTest/Resources/SolutionFiles/VisualBasic.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "VisualBasicProject", "VisualBasicProject\VisualBasicProject.vbproj", "{AC25ECDA-DE94-4FCF-A688-EB3A2BE3670C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC25ECDA-DE94-4FCF-A688-EB3A2BE3670C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC25ECDA-DE94-4FCF-A688-EB3A2BE3670C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC25ECDA-DE94-4FCF-A688-EB3A2BE3670C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC25ECDA-DE94-4FCF-A688-EB3A2BE3670C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/Workspaces/MSBuildTest/TestFiles/Resources.cs b/src/Workspaces/MSBuildTest/TestFiles/Resources.cs index 31cfb2b0c5509..f33a67f50351b 100644 --- a/src/Workspaces/MSBuildTest/TestFiles/Resources.cs +++ b/src/Workspaces/MSBuildTest/TestFiles/Resources.cs @@ -103,6 +103,7 @@ public static class SolutionFiles public static string NonExistentProject => GetText("SolutionFiles.NonExistentProject.sln"); public static string ProjectLoadErrorOnMissingDebugType => GetText("SolutionFiles.ProjectLoadErrorOnMissingDebugType.sln"); public static string SolutionFolder => GetText("SolutionFiles.SolutionFolder.sln"); + public static string VisualBasic => GetText("SolutionFiles.VisualBasic.sln"); public static string VB_and_CSharp => GetText("SolutionFiles.VB_and_CSharp.sln"); } @@ -160,6 +161,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 +188,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/Utilities/VisualStudioMSBuildInstalled.cs b/src/Workspaces/MSBuildTest/Utilities/VisualStudioMSBuildInstalled.cs index 3e0f0819c4b85..4f001750ba6d2 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.Linq; using Microsoft.Build.Locator; using Roslyn.Test.Utilities; @@ -12,37 +11,41 @@ namespace Microsoft.CodeAnalysis.MSBuild.UnitTests internal class VisualStudioMSBuildInstalled : ExecutionCondition { #if NET472_OR_GREATER + private static readonly VisualStudioInstance? s_instance; private readonly Version _minimumVersion; 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 public VisualStudioMSBuildInstalled() -#if NET472_OR_GREATER - : this(new Version(16, 9)) -#endif + : this(new Version(17, 0)) { } -#if NET472_OR_GREATER internal VisualStudioMSBuildInstalled(Version minimumVersion) { +#if NET472_OR_GREATER _minimumVersion = minimumVersion; - } #endif + } public override bool ShouldSkip #if NET472_OR_GREATER diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 20de2f3eca292..2d0e3698d0d9a 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -247,6 +247,45 @@ public async Task TestCompilationOutputInfo() Assert.Equal("VisualBasicProject.dll", Path.GetFileName(p2.CompilationOutputInfo.AssemblyPath)); } + [ConditionalTheory(typeof(VisualStudioMSBuildInstalled))] + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public async Task TestChecksumAlgorithm_NonDefault(string language) + { + var files = language == LanguageNames.CSharp ? + GetSimpleCSharpSolutionFiles().WithFile(@"CSharpProject\CSharpProject.csproj", Resources.ProjectFiles.CSharp.WithChecksumAlgorithm) : + GetSimpleVisualBasicSolutionFiles().WithFile(@"VisualBasicProject\VisualBasicProject.vbproj", Resources.ProjectFiles.VisualBasic.WithChecksumAlgorithm); + + CreateFiles(files); + 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)); + } + + [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 project = sol.Projects.First(p => p.Language == language); + + Assert.Equal(SourceHashAlgorithms.Default, project.State.ChecksumAlgorithm); + + Assert.All(project.Documents, d => Assert.Equal(SourceHashAlgorithms.Default, d.GetTextSynchronously(default).ChecksumAlgorithm)); + } + [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] public async Task TestCrossLanguageReferencesUsesInMemoryGeneratedMetadata() { @@ -923,30 +962,22 @@ public async Task TestOpenSolution_WithTemporaryLockedFile_SucceedsWithoutFailur } [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] - public async Task TestOpenSolution_WithLockedFile_FailsWithFailureEvent() + public async Task TestOpenSolution_WithLockedFile_LoadsWithEmptyText() { - // when skipped we should see a diagnostic for the invalid project - CreateFiles(GetSimpleCSharpSolutionFiles()); var solutionFilePath = GetSolutionFileName(@"TestSolution.sln"); using var workspace = CreateMSBuildWorkspace(throwOnWorkspaceFailed: false); + // open source file so it cannot be read by workspace; var sourceFile = GetSolutionFileName(@"CSharpProject\CSharpClass.cs"); - var file = File.Open(sourceFile, FileMode.Open, FileAccess.Write, FileShare.None); - try - { - var solution = await workspace.OpenSolutionAsync(solutionFilePath); - var doc = solution.Projects.First().Documents.First(d => d.FilePath == sourceFile); - var text = await doc.GetTextAsync(); - Assert.Empty(text.ToString()); - } - finally - { - file.Close(); - } + using var file = File.Open(sourceFile, FileMode.Open, FileAccess.Write, FileShare.None); - Assert.Equal(WorkspaceDiagnosticKind.Failure, workspace.Diagnostics.Single().Kind); + var solution = await workspace.OpenSolutionAsync(solutionFilePath); + var doc = solution.Projects.First().Documents.First(d => d.FilePath == sourceFile); + var text = await doc.GetTextAsync(); + Assert.Empty(text.ToString()); + Assert.NotNull(await doc.State.GetLoadDiagnosticAsync(CancellationToken.None)); } [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] @@ -2326,10 +2357,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(new LoadTextOptions(SourceHashAlgorithms.Default), 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(new LoadTextOptions(SourceHashAlgorithms.Default), CancellationToken.None); Assert.Contains("Window", atav.Text.ToString(), StringComparison.Ordinal); } diff --git a/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs b/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs index 8a6c74f4292bd..2916c3dbecd5f 100644 --- a/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs +++ b/src/Workspaces/MSBuildTest/WorkspaceTestBase.cs @@ -104,6 +104,24 @@ protected static FileSet GetSimpleCSharpSolutionFiles() (@"CSharpProject\Properties\AssemblyInfo.cs", Resources.SourceFiles.CSharp.AssemblyInfo)); } + protected static FileSet GetSimpleVisualBasicSolutionFiles() + { + return new FileSet( + (@"NuGet.Config", Resources.NuGet_Config), + (@"Directory.Build.props", Resources.Directory_Build_props), + (@"Directory.Build.targets", Resources.Directory_Build_targets), + (@"TestSolution.sln", Resources.SolutionFiles.VisualBasic), + (@"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( diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 2bc3e967a9a3b..11331ea45542b 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,21 @@ 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)); + 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 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/Core/BrokeredServiceConnection.cs b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs index 420a9e69d81ef..9e269cdbceb99 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO.Pipelines; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; @@ -122,6 +124,15 @@ public override async ValueTask> TryInvokeAsync(Func< } } + public override async IAsyncEnumerable TryInvokeStreamAsync( + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using var rental = await RentServiceAsync(cancellationToken).ConfigureAwait(false); + await foreach (var item in invocation(rental.Service, cancellationToken).ConfigureAwait(false)) + yield return item; + } + // no solution, callback public override async ValueTask TryInvokeAsync(Func invocation, CancellationToken cancellationToken) @@ -190,6 +201,17 @@ public override async ValueTask> TryInvokeAsync(Solut } } + public override async IAsyncEnumerable TryInvokeStreamAsync( + Solution solution, + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using var scope = await _solutionAssetStorage.StoreAssetsAsync(solution, cancellationToken).ConfigureAwait(false); + using var rental = await RentServiceAsync(cancellationToken).ConfigureAwait(false); + await foreach (var value in invocation(rental.Service, scope.SolutionChecksum, cancellationToken)) + yield return value; + } + // project, no callback public override async ValueTask TryInvokeAsync(Project project, Func invocation, CancellationToken cancellationToken) @@ -223,6 +245,17 @@ public override async ValueTask> TryInvokeAsync(Proje } } + public override async IAsyncEnumerable TryInvokeStreamAsync( + Project project, + Func> invocation, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using var scope = await _solutionAssetStorage.StoreAssetsAsync(project, cancellationToken).ConfigureAwait(false); + using var rental = await RentServiceAsync(cancellationToken).ConfigureAwait(false); + await foreach (var item in invocation(rental.Service, scope.SolutionChecksum, cancellationToken).ConfigureAwait(false)) + yield return item; + } + // solution, callback public override async ValueTask TryInvokeAsync(Solution solution, Func invocation, CancellationToken cancellationToken) @@ -299,80 +332,27 @@ public override async ValueTask> TryInvokeAsync(Proje } } - // streaming - - /// The service instance. - /// A callback to asynchronously write data. The callback is required to complete the - /// except in cases where the callback throws an exception. - /// A callback to asynchronously read data. The callback is allowed, but not required, to - /// complete the . - /// A cancellation token the operation will observe. - internal static async ValueTask InvokeStreamingServiceAsync( - TService service, - Func invocation, - Func> reader, - CancellationToken cancellationToken) + // multi-solution, no callback + + public override async ValueTask TryInvokeAsync(Solution solution1, Solution solution2, Func invocation, CancellationToken cancellationToken) { - // We can cancel at entry, but once the pipe operations are scheduled we rely on both operations running to - // avoid deadlocks (the exception handler in 'writerTask' ensures progress is made in 'readerTask'). - cancellationToken.ThrowIfCancellationRequested(); - var mustNotCancelToken = CancellationToken.None; - - // After this point, the full cancellation sequence is as follows: - // 1. 'cancellationToken' indicates cancellation is requested - // 2. 'invocation' and 'readerTask' have cancellation requested - // 3. 'invocation' stops writing to 'pipe.Writer' - // 4. 'pipe.Writer' is completed - // 5. 'readerTask' continues reading until EndOfStreamException (workaround for https://github.com/AArnott/Nerdbank.Streams/issues/361) - // 6. 'pipe.Reader' is completed - // 7. OperationCanceledException is thrown back to the caller - - var pipe = new Pipe(); - - // Create new tasks that both start executing, rather than invoking the delegates directly - // to make sure both invocation and reader start executing and transfering data. - - var writerTask = Task.Run(async () => - { - try - { - await invocation(service, pipe.Writer, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) - { - // Ensure that the writer is complete if an exception is thrown - // before the writer is passed to the RPC proxy. Once it's passed to the proxy - // the proxy should complete it as soon as the remote side completes it. - await pipe.Writer.CompleteAsync(e).ConfigureAwait(false); - - throw; - } - }, mustNotCancelToken); - - var readerTask = Task.Run( - async () => - { - Exception? exception = null; - - try - { - return await reader(pipe.Reader, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when ((exception = e) == null) - { - throw ExceptionUtilities.Unreachable; - } - finally - { - await pipe.Reader.CompleteAsync(exception).ConfigureAwait(false); - } - }, mustNotCancelToken); - - await Task.WhenAll(writerTask, readerTask).ConfigureAwait(false); - - return readerTask.Result; + try + { + using var scope1 = await _solutionAssetStorage.StoreAssetsAsync(solution1, cancellationToken).ConfigureAwait(false); + using var scope2 = await _solutionAssetStorage.StoreAssetsAsync(solution2, cancellationToken).ConfigureAwait(false); + using var rental = await RentServiceAsync(cancellationToken).ConfigureAwait(false); + await invocation(rental.Service, scope1.SolutionChecksum, scope2.SolutionChecksum, cancellationToken).ConfigureAwait(false); + return true; + } + catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) + { + OnUnexpectedException(exception, cancellationToken); + return false; + } } + // Exceptions + private bool ReportUnexpectedException(Exception exception, CancellationToken cancellationToken) { if (exception is OperationCanceledException) diff --git a/src/Workspaces/Remote/Core/ISolutionAssetProvider.cs b/src/Workspaces/Remote/Core/ISolutionAssetProvider.cs index 18daa75b786a4..d4ab9ac7dafb4 100644 --- a/src/Workspaces/Remote/Core/ISolutionAssetProvider.cs +++ b/src/Workspaces/Remote/Core/ISolutionAssetProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.IO; using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; @@ -18,6 +16,9 @@ internal interface ISolutionAssetProvider /// /// Streams serialized assets into the given stream. /// + /// The writer to write the assets into. Implementations of this method must call on it (in the event of failure or success). Failing to do so will lead to + /// hangs on the code that reads from the corresponding side of this. ValueTask GetAssetsAsync(PipeWriter pipeWriter, Checksum solutionChecksum, Checksum[] checksums, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Remote/Core/RemoteCallback.cs b/src/Workspaces/Remote/Core/RemoteCallback.cs index 5c9cdadd97a99..e40309b6cc045 100644 --- a/src/Workspaces/Remote/Core/RemoteCallback.cs +++ b/src/Workspaces/Remote/Core/RemoteCallback.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.ServiceHub.Framework; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using StreamJsonRpc; @@ -67,7 +68,7 @@ public async ValueTask InvokeAsync(Func invocat } catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { - throw OnUnexpectedException(exception, cancellationToken); + throw new OperationCanceledIgnoringCallerTokenException(exception); } } @@ -82,15 +83,20 @@ public async ValueTask InvokeAsync(Func - /// Invokes API on the callback object hosted in the original process (usually devenv) associated with the currently executing brokered service hosted in ServiceHub process. - /// The API streams results back to the caller. + /// Invokes API on the callback object hosted in the original process (usually devenv) associated with the + /// currently executing brokered service hosted in ServiceHub process. The API streams results back to the + /// caller. /// - /// + /// A callback to asynchronously write data. The callback should always the . If it does not then reading will hang + /// A callback to asynchronously read data. The callback should not complete the , but no harm will happen if it does. + /// A cancellation token the operation will observe. public async ValueTask InvokeAsync( Func invocation, Func> reader, @@ -98,11 +104,95 @@ public async ValueTask InvokeAsync( { try { - return await BrokeredServiceConnection.InvokeStreamingServiceAsync(_callback, invocation, reader, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + var pipe = new Pipe(); + + // Kick off the work to do the writing to the pipe asynchronously. It will start hot and will be able + // to do work as the reading side attempts to pull in the data it is writing. + + var writeTask = WriteAsync(_callback, pipe.Writer); + var readTask = ReadAsync(pipe.Reader); + + // Note: waiting on the write-task is not strictly necessary. The read-task cannot complete unless it + // the write-task completes (or it faults for some reason). However, it's nice and clean to just not + // use fire-and-forget here and avoids us having to consider things like async-tracking-tokens for + // testing purposes. + await Task.WhenAll(writeTask, readTask).ConfigureAwait(false); + return await readTask.ConfigureAwait(false); } catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { - throw OnUnexpectedException(exception, cancellationToken); + throw new OperationCanceledIgnoringCallerTokenException(exception); + } + + async Task WriteAsync(T service, PipeWriter pipeWriter) + { + Exception? exception = null; + try + { + // Intentionally yield this thread so that the caller can proceed concurrently and start reading. + // This is not strictly necessary (as we know the writer will always call FlushAsync()), but it is nice + // as it allows both to proceed concurrently on the initial writing/reading. + await Task.Yield(); + + await invocation(service, pipeWriter, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) when ((exception = ex) == null) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + // Absolutely do not Complete/CompleteAsync the writer here *unless* an exception occurred. The + // writer is passed to StreamJsonRPC which takes ownership of it. The *inside* of that rpc is + // responsible for Completing the writer *it* is passed, which will signal the completion of the + // writer we have here. + // + // We *do* need to complete this writer in the event if an exception as that may have happened + // *prior* to even issuing the rpc. If we don't complete the writer we will hang. If the exception + // happened within the RPC the writer may already be completed, but it's fine for us to complete it + // a second time. + // + // The reason is *not* fine for us to complete the writer in a non-exception event is that it legal + // (and is the case in practice) that the code in StreamJsonRPC may still be using it (see + // https://github.com/AArnott/Nerdbank.Streams/blob/dafeb5846702bc29e261c9ddf60f42feae01654c/src/Nerdbank.Streams/PipeExtensions.cs#L428) + // where the writer may be advanced in an independent Task even once the rpc message has returned to + // the caller (us). + // + // NOTE: it is intentinonal that the try/catch pattern here does NOT match the one in ReadAsync. There + // are very different semantics around each. The writer code passes ownership to StreamJsonRPC, while + // the reader code does not. As such, the reader code is responsible for completing the reader in all + // cases, whereas the writer code only completes when faulting. + + // DO NOT REMOVE THIS NULL CHECK WITHOUT DEEP AND CAREFUL REVIEW. + if (exception != null) + await pipeWriter.CompleteAsync(exception).ConfigureAwait(false); + } + } + + async Task ReadAsync(PipeReader pipeReader) + { + // NOTE: it is intentional that the try/catch pattern here does NOT match the one in WriteAsync. There + // are very different semantics around each. The writer code passes ownership to StreamJsonRPC, while + // the reader code does not. As such, the reader code is responsible for completing the reader in all + // cases, whereas the writer code only completes when faulting. + + Exception? exception = null; + try + { + return await reader(pipeReader, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) when ((exception = ex) == null) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + // ensure we always complete the reader so the pipe can clean up all its resources. in the case of + // an exception, attempt to complete the reader with that as well as that will tear down the writer + // allowing it to stop writing and allowing the pipe to be cleaned up. + await pipeReader.CompleteAsync(exception).ConfigureAwait(false); + } } } @@ -129,34 +219,21 @@ private static bool ReportUnexpectedException(Exception exception, CancellationT return false; } - // Log unexpected state where a cancellation exception occurs without being requested. + // Log unexpected state where a cancellation exception occurs without being requested. This will return + // 'true' and caller will convert this to an acceptable cancellation token that won't cause a second + // NFW. return FatalError.ReportAndCatch(exception); } - // When a connection is dropped we can see ConnectionLostException even though CancelLocallyInvokedMethodsWhenConnectionIsClosed is set. - // That's because there might be a delay between the JsonRpc detecting the disconnect and the call attempting to send a message. - // Catch the ConnectionLostException exception here and convert it to OperationCanceledException. + // When a connection is dropped we can see ConnectionLostException even though + // CancelLocallyInvokedMethodsWhenConnectionIsClosed is set. That's because there might be a delay between + // the JsonRpc detecting the disconnect and the call attempting to send a message. Catch the + // ConnectionLostException exception here and convert it to OperationCanceledException. if (exception is ConnectionLostException) - { return true; - } // Indicates bug on client side or in serialization, report NFW and propagate the exception. return FatalError.ReportAndPropagate(exception); } - - private static Exception OnUnexpectedException(Exception exception, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (exception is ConnectionLostException) - { - throw new OperationCanceledIgnoringCallerTokenException(exception); - } - - // If this is hit the cancellation token passed to the service implementation did not use the correct token, - // and the resulting exception was not a ConnectionLostException. - return ExceptionUtilities.Unreachable; - } } } diff --git a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs index 9f269ae04024a..e7d4ca03803b1 100644 --- a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs +++ b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs @@ -12,16 +12,15 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; +using Nerdbank.Streams; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { internal static class RemoteHostAssetSerialization { - internal static readonly PipeOptions PipeOptionsWithUnlimitedWriterBuffer = new(pauseWriterThreshold: long.MaxValue); - - public static void WriteData( - ObjectWriter writer, + public static async ValueTask WriteDataAsync( + Stream stream, SolutionAsset? singleAsset, IReadOnlyDictionary? assetMap, ISerializerService serializer, @@ -30,6 +29,8 @@ public static void WriteData( Checksum[] checksums, CancellationToken cancellationToken) { + using var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken); + // This information is not actually needed on the receiving end. However, we still send it so that the // receiver can assert that both sides are talking about the same solution snapshot and no weird invariant // breaks have occurred. @@ -55,8 +56,16 @@ public static void WriteData( foreach (var (checksum, asset) in assetMap) { WriteAsset(writer, serializer, context, checksum, asset, cancellationToken); + + // We flush after each item as that forms a reasonably sized chunk of data to want to then send over the + // pipe for the reader on the other side to read. This allows the item-writing to remain entirely + // synchronous without any blocking on async flushing, while also ensuring that we're not buffering the + // entire stream of data into the pipe before it gets sent to the other side. + await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } + return; + static void WriteAsset(ObjectWriter writer, ISerializerService serializer, SolutionReplicationContext context, Checksum checksum, SolutionAsset asset, CancellationToken cancellationToken) { Debug.Assert(asset.Kind != WellKnownSynchronizationKind.Null, "We should not be sending null assets"); @@ -74,81 +83,8 @@ static void WriteAsset(ObjectWriter writer, ISerializerService serializer, Solut public static async ValueTask> ReadDataAsync( PipeReader pipeReader, Checksum solutionChecksum, ISet checksums, ISerializerService serializerService, CancellationToken cancellationToken) { - // We can cancel at entry, but once the pipe operations are scheduled we rely on both operations running to - // avoid deadlocks (the exception handler in 'copyTask' ensures progress is made in the blocking read). - cancellationToken.ThrowIfCancellationRequested(); - var mustNotCancelToken = CancellationToken.None; - - // Workaround for https://github.com/AArnott/Nerdbank.Streams/issues/361 - var mustNotCancelUntilBugFix = CancellationToken.None; - - // Workaround for ObjectReader not supporting async reading. - // Unless we read from the RPC stream asynchronously and with cancallation support we might deadlock when the server cancels. - // https://github.com/dotnet/roslyn/issues/47861 - - // Use local pipe to avoid blocking the current thread on networking IO. - var localPipe = new Pipe(PipeOptionsWithUnlimitedWriterBuffer); - - Exception? copyException = null; - - // start a task on a thread pool thread copying from the RPC pipe to a local pipe: - var copyTask = Task.Run(async () => - { - try - { - await pipeReader.CopyToAsync(localPipe.Writer, mustNotCancelUntilBugFix).ConfigureAwait(false); - } - catch (Exception e) - { - copyException = e; - } - finally - { - await localPipe.Writer.CompleteAsync(copyException).ConfigureAwait(false); - } - }, mustNotCancelToken); - - // blocking read from the local pipe on the current thread: - try - { - using var stream = localPipe.Reader.AsStream(leaveOpen: false); - return ReadData(stream, solutionChecksum, checksums, serializerService, mustNotCancelUntilBugFix); - } - catch (EndOfStreamException) when (IsEndOfStreamExceptionExpected(copyException, cancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - - throw copyException ?? ExceptionUtilities.Unreachable; - } - finally - { - // Make sure to complete the copy and pipes before returning, otherwise the caller could complete the - // reader and/or writer while they are still in use. - await copyTask.ConfigureAwait(false); - } - - // Local functions - static bool IsEndOfStreamExceptionExpected(Exception? copyException, CancellationToken cancellationToken) - { - // The local pipe is only closed in the 'finally' block of 'copyTask'. If the reader fails with an - // EndOfStreamException, we known 'copyTask' has already completed its work. - if (cancellationToken.IsCancellationRequested) - { - // The writer closed early due to a cancellation request. - return true; - } - - if (copyException is not null) - { - // An exception occurred while attempting to copy data to the local pipe. Catch and throw the - // exception that occurred during that copy operation. - return true; - } - - // The reader attempted to read more data than was copied to the local pipe. Avoid catching the - // exception to reveal the faulty read stack in telemetry. - return false; - } + using var stream = await pipeReader.AsPrebufferedStreamAsync(cancellationToken).ConfigureAwait(false); + return ReadData(stream, solutionChecksum, checksums, serializerService, cancellationToken); } public static ImmutableArray<(Checksum, object)> ReadData(Stream stream, Checksum solutionChecksum, ISet checksums, ISerializerService serializerService, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx index ab3bcec9f57f9..cdea2b318a84f 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx +++ b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx @@ -213,4 +213,10 @@ Stack Trace Explorer + + Solution Events + + + Unit testing search + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs index e6ede9554549f..5e84344ffa380 100644 --- a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs +++ b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Runtime.Serialization; +using System.Text; using MessagePack; using MessagePack.Formatters; using MessagePack.Resolvers; @@ -24,6 +26,7 @@ internal sealed class MessagePackFormatters { internal static readonly ImmutableArray Formatters = ImmutableArray.Create( ProjectIdFormatter.Instance, + EncodingFormatter.Instance, // ForceTypelessFormatter needs to be listed here for each Roslyn abstract type T that is being serialized OOP. // TODO: add a resolver that provides these https://github.com/dotnet/roslyn/issues/60724 new ForceTypelessFormatter(), @@ -107,5 +110,79 @@ public void Serialize(ref MessagePackWriter writer, ProjectId? value, MessagePac } } } + + /// + /// Supports (de)serialization of that do not customize or . + /// The fallback will be discarded if the has any. + /// + /// + /// Only supports (de)serializing values that are statically typed to . + /// This is important as we can't assume anything about arbitrary subtypes of + /// and can only return general from the deserializer. + /// + internal sealed class EncodingFormatter : IMessagePackFormatter + { + public static readonly EncodingFormatter Instance = new(); + + public Encoding? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + if (reader.TryReadNil()) + { + return null; + } + + var kind = (TextEncodingKind)reader.ReadByte(); + if (kind != TextEncodingKind.None) + { + return kind.GetEncoding(); + } + + var codePage = reader.ReadInt32(); + if (codePage > 0) + { + return Encoding.GetEncoding(codePage); + } + + var name = reader.ReadString(); + return Encoding.GetEncoding(name); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, Encoding? value, MessagePackSerializerOptions options) + { + try + { + if (value is null) + { + writer.WriteNil(); + } + else if (value.TryGetEncodingKind(out var kind)) + { + Debug.Assert(kind != TextEncodingKind.None); + writer.WriteUInt8((byte)kind); + } + else + { + writer.WriteUInt8((byte)TextEncodingKind.None); + var codePage = value.CodePage; + writer.Write(codePage); + if (codePage <= 0) + { + writer.Write(value.WebName); + } + } + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + } } } diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index 07cb09bcd4f0f..5b6932adfbef0 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EncapsulateField; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host; @@ -24,6 +25,7 @@ using Microsoft.CodeAnalysis.NavigationBar; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.LegacySolutionEvents; using Microsoft.CodeAnalysis.StackTraceExplorer; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.TaskList; @@ -52,7 +54,7 @@ internal sealed class ServiceDescriptors (typeof(IRemoteAssetSynchronizationService), null), (typeof(IRemoteAsynchronousOperationListenerService), null), (typeof(IRemoteTaskListService), null), - (typeof(IRemoteDesignerAttributeDiscoveryService), typeof(IRemoteDesignerAttributeDiscoveryService.ICallback)), + (typeof(IRemoteDesignerAttributeDiscoveryService), null), (typeof(IRemoteDiagnosticAnalyzerService), null), (typeof(IRemoteSemanticClassificationService), null), (typeof(IRemoteDocumentHighlightsService), null), @@ -61,7 +63,7 @@ internal sealed class ServiceDescriptors (typeof(IRemoteConvertTupleToStructCodeRefactoringService), typeof(IRemoteConvertTupleToStructCodeRefactoringService.ICallback)), (typeof(IRemoteSymbolFinderService), typeof(IRemoteSymbolFinderService.ICallback)), (typeof(IRemoteFindUsagesService), typeof(IRemoteFindUsagesService.ICallback)), - (typeof(IRemoteNavigateToSearchService), typeof(IRemoteNavigateToSearchService.ICallback)), + (typeof(IRemoteNavigateToSearchService), null), (typeof(IRemoteNavigationBarItemService), null), (typeof(IRemoteMissingImportDiscoveryService), typeof(IRemoteMissingImportDiscoveryService.ICallback)), (typeof(IRemoteSymbolSearchUpdateService), typeof(IRemoteSymbolSearchUpdateService.ICallback)), @@ -75,7 +77,9 @@ internal sealed class ServiceDescriptors (typeof(IRemoteUnusedReferenceAnalysisService), null), (typeof(IRemoteProcessTelemetryService), null), (typeof(IRemoteCompilationAvailableService), null), + (typeof(IRemoteLegacySolutionEventsAggregationService), null), (typeof(IRemoteStackTraceExplorerService), null), + (typeof(IRemoteUnitTestingSearchService), null), }); internal readonly RemoteSerializationOptions Options; @@ -134,7 +138,7 @@ public ServiceDescriptor GetServiceDescriptor(Type serviceType, RemoteProcessCon RemoteProcessConfiguration.Core => descriptorCoreClr64, RemoteProcessConfiguration.ServerGC => descriptor64ServerGC, RemoteProcessConfiguration.Core | RemoteProcessConfiguration.ServerGC => descriptorCoreClr64ServerGC, - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; } diff --git a/src/Workspaces/Remote/Core/SolutionAssetProvider.cs b/src/Workspaces/Remote/Core/SolutionAssetProvider.cs index 8bbdef8ca73b5..ca12592389e62 100644 --- a/src/Workspaces/Remote/Core/SolutionAssetProvider.cs +++ b/src/Workspaces/Remote/Core/SolutionAssetProvider.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Collections.Generic; +using System.IO; using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; @@ -32,6 +34,26 @@ public SolutionAssetProvider(SolutionServices services) } public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, Checksum solutionChecksum, Checksum[] checksums, CancellationToken cancellationToken) + { + // The responsibility is on us (as per the requirements of RemoteCallback.InvokeAsync) to Complete the + // pipewriter. This will signal to streamjsonrpc that the writer passed into it is complete, which will + // allow the calling side know to stop reading results. + Exception? exception = null; + try + { + await GetAssetsWorkerAsync(pipeWriter, solutionChecksum, checksums, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) when ((exception = ex) == null) + { + throw ExceptionUtilities.Unreachable(); + } + finally + { + await pipeWriter.CompleteAsync(exception).ConfigureAwait(false); + } + } + + private async ValueTask GetAssetsWorkerAsync(PipeWriter pipeWriter, Checksum solutionChecksum, Checksum[] checksums, CancellationToken cancellationToken) { var assetStorage = _services.GetRequiredService().AssetStorage; var serializer = _services.GetRequiredService(); @@ -50,59 +72,157 @@ public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, Checksum solutionCh assetMap = await scope.GetAssetsAsync(checksums, cancellationToken).ConfigureAwait(false); } - // We can cancel early, but once the pipe operations are scheduled we rely on both operations running to - // avoid deadlocks (the exception handler in 'task1' ensures progress is made in 'task2'). cancellationToken.ThrowIfCancellationRequested(); - var mustNotCancelToken = CancellationToken.None; - - // Work around the lack of async stream writing in ObjectWriter, which is required when writing to the RPC - // pipe. Run two tasks - the first synchronously writes to a local pipe and the second asynchronously - // transfers the data to the RPC pipe. - // - // Configure the pipe to never block on write (waiting for the reader to read). This prevents deadlocks but - // might result in more (non-contiguous) memory allocated for the underlying buffers. The amount of memory - // is bounded by the total size of the serialized assets. - var localPipe = new Pipe(RemoteHostAssetSerialization.PipeOptionsWithUnlimitedWriterBuffer); - - var task1 = Task.Run(() => - { - try - { - var stream = localPipe.Writer.AsStream(leaveOpen: false); - using var writer = new ObjectWriter(stream, leaveOpen: false, cancellationToken); - RemoteHostAssetSerialization.WriteData(writer, singleAsset, assetMap, serializer, scope.ReplicationContext, solutionChecksum, checksums, cancellationToken); - } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) - { - // no-op - } - }, mustNotCancelToken); - - // Complete RPC once we send the initial piece of data and start waiting for the writer to send more, - // so the client can start reading from the stream. Once CopyPipeDataAsync completes the pipeWriter - // the corresponding client-side pipeReader will complete and the data transfer will be finished. - var task2 = CopyPipeDataAsync(); - - await Task.WhenAll(task1, task2).ConfigureAwait(false); - - async Task CopyPipeDataAsync() - { - Exception? exception = null; - try - { - await localPipe.Reader.CopyToAsync(pipeWriter, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) - { - FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken); - exception = e; - } - finally - { - await localPipe.Reader.CompleteAsync(exception).ConfigureAwait(false); - await pipeWriter.CompleteAsync(exception).ConfigureAwait(false); - } + + using var stream = new PipeWriterStream(pipeWriter); + await RemoteHostAssetSerialization.WriteDataAsync( + stream, singleAsset, assetMap, serializer, scope.ReplicationContext, + solutionChecksum, checksums, cancellationToken).ConfigureAwait(false); + + // Ensure any last data written into the stream makes it into the pipe. + await stream.FlushAsync(cancellationToken).ConfigureAwait(false); + } + + /// + /// Simple port of + /// https://github.com/AArnott/Nerdbank.Streams/blob/dafeb5846702bc29e261c9ddf60f42feae01654c/src/Nerdbank.Streams/BufferWriterStream.cs#L16. + /// Wraps a in a interface. Preferred over as that API produces a stream that will synchronously flush after + /// every write. That's undesirable as that will then block a thread pool thread on the actual + /// asynchronous flush call to the underlying PipeWriter + /// + /// + /// Note: this stream does not have to the underlying it + /// is holding onto (including within , , or ). + /// Responsibility for that is solely in the hands of . + /// + private class PipeWriterStream : Stream, IDisposableObservable + { + private readonly PipeWriter _writer; + + public bool IsDisposed { get; private set; } + + public override bool CanRead => false; + public override bool CanSeek => false; + public override bool CanWrite => !this.IsDisposed; + + internal PipeWriterStream(PipeWriter writer) + { + _writer = writer; + } + + protected override void Dispose(bool disposing) + { + this.IsDisposed = true; + base.Dispose(disposing); + + // DO NOT CALL .Complete on the PipeWriter here (see remarks on type). + } + + private Exception ThrowDisposedOr(Exception ex) + { + Verify.NotDisposed(this); + throw ex; + } + + /// + /// Intentionally a no op. We know that we and + /// will call at appropriate times to ensure data is being sent through the writer + /// at a reasonable cadence (once per asset). + /// + public override void Flush() + { + Verify.NotDisposed(this); + + // DO NOT CALL .Complete on the PipeWriter here (see remarks on type). + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + await _writer.FlushAsync(cancellationToken).ConfigureAwait(false); + + // DO NOT CALL .Complete on the PipeWriter here (see remarks on type). } + + public override void Write(byte[] buffer, int offset, int count) + { + Requires.NotNull(buffer, nameof(buffer)); + Verify.NotDisposed(this); + + var span = _writer.GetSpan(count); + buffer.AsSpan(offset, count).CopyTo(span); + _writer.Advance(count); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + this.Write(buffer, offset, count); + return Task.CompletedTask; + } + + public override void WriteByte(byte value) + { + Verify.NotDisposed(this); + var span = _writer.GetSpan(1); + span[0] = value; + _writer.Advance(1); + } + +#if !NETSTANDARD + + public override void Write(ReadOnlySpan buffer) + { + Verify.NotDisposed(this); + var span = _writer.GetSpan(buffer.Length); + buffer.CopyTo(span); + _writer.Advance(buffer.Length); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + this.Write(buffer.Span); + return default; + } + +#endif + + #region read/seek api (not supported) + + public override long Length => throw this.ThrowDisposedOr(new NotSupportedException()); + public override long Position + { + get => throw this.ThrowDisposedOr(new NotSupportedException()); + set => this.ThrowDisposedOr(new NotSupportedException()); + } + + public override int Read(byte[] buffer, int offset, int count) + => throw this.ThrowDisposedOr(new NotSupportedException()); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => throw this.ThrowDisposedOr(new NotSupportedException()); + +#if !NETSTANDARD + + public override int Read(Span buffer) + => throw this.ThrowDisposedOr(new NotSupportedException()); + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) + => throw this.ThrowDisposedOr(new NotSupportedException()); + +#endif + + public override int ReadByte() + => throw this.ThrowDisposedOr(new NotSupportedException()); + + public override long Seek(long offset, SeekOrigin origin) + => throw this.ThrowDisposedOr(new NotSupportedException()); + + public override void SetLength(long value) + => this.ThrowDisposedOr(new NotSupportedException()); + + #endregion } } } diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf index cf031a87c3cc5..cb6ddeb449240 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf @@ -77,6 +77,11 @@ Míra dědičnosti + + Solution Events + Solution Events + + Missing import discovery Chybí zjišťování importů @@ -137,6 +142,11 @@ Seznam úkolů + + Unit testing search + Unit testing search + + Unused reference analysis Nepoužité referenční analýzy diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf index a41ffec9cff5e..bb8eef1384dd9 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf @@ -77,6 +77,11 @@ Vererbungsrand + + Solution Events + Solution Events + + Missing import discovery Fehlende Importermittlung @@ -137,6 +142,11 @@ Aufgabenliste + + Unit testing search + Unit testing search + + Unused reference analysis Nicht verwendete Referenzanalyse diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf index f1f6d11c8e4af..8f54f8564fa45 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf @@ -77,6 +77,11 @@ Margen de herencia + + Solution Events + Solution Events + + Missing import discovery Detección de importaciones que faltan @@ -137,6 +142,11 @@ Lista de tareas + + Unit testing search + Unit testing search + + Unused reference analysis Análisis de referencia inutilizado diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf index aec2ac00d5a87..d033ef9b437ea 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf @@ -77,6 +77,11 @@ Marge d’héritage + + Solution Events + Solution Events + + Missing import discovery Détection d'importation manquante @@ -137,6 +142,11 @@ Liste de tâches + + Unit testing search + Unit testing search + + Unused reference analysis Analyse de référence inutilisée diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf index fa3744fddb526..dae2f9907a6b7 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf @@ -77,6 +77,11 @@ Margine di ereditarietà + + Solution Events + Solution Events + + Missing import discovery Individuazione delle importazioni mancanti @@ -137,6 +142,11 @@ Elenco attività + + Unit testing search + Unit testing search + + Unused reference analysis Analisi di riferimento non utilizzata diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf index 95712c44f4383..bf72695cd71f3 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf @@ -77,6 +77,11 @@ 継承の余白 + + Solution Events + Solution Events + + Missing import discovery 不足しているインポートの検出 @@ -137,6 +142,11 @@ タスク一覧 + + Unit testing search + Unit testing search + + Unused reference analysis 未使用の参照分析 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf index 6735454fe1bad..d1c26680cd272 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf @@ -77,6 +77,11 @@ 상속 여백 + + Solution Events + Solution Events + + Missing import discovery 가져오기 검색 없음 @@ -137,6 +142,11 @@ 작업 목록 + + Unit testing search + Unit testing search + + Unused reference analysis 사용되지 않는 참조 분석 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf index 74620988a8544..d2c465de3d2b3 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf @@ -77,6 +77,11 @@ Margines dziedziczenia + + Solution Events + Solution Events + + Missing import discovery Odnajdywanie brakujących importów @@ -137,6 +142,11 @@ Lista zadań + + Unit testing search + Unit testing search + + Unused reference analysis Nieużywane analizy referencyjne diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf index 9bf83e2aa06c5..f0a4ca34897b2 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf @@ -77,6 +77,11 @@ Margem de herança + + Solution Events + Solution Events + + Missing import discovery Descoberta de importação ausente @@ -137,6 +142,11 @@ Lista de tarefas + + Unit testing search + Unit testing search + + Unused reference analysis Análise de referência não usada diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf index 4e2183175f047..1ad9010a0891c 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf @@ -77,6 +77,11 @@ Граница наследования + + Solution Events + Solution Events + + Missing import discovery Отсутствует обнаружение импорта @@ -137,6 +142,11 @@ Список задач + + Unit testing search + Unit testing search + + Unused reference analysis Анализ неиспользуемых ссылок diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf index dd25b4f506032..f54ca060716f8 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf @@ -77,6 +77,11 @@ Devralma boşluğu + + Solution Events + Solution Events + + Missing import discovery İçeri aktarma bulma eksik @@ -137,6 +142,11 @@ Görev listesi + + Unit testing search + Unit testing search + + Unused reference analysis Kullanılmayan başvuru analizi diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf index 7181b7f881334..132e365f15f13 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf @@ -77,6 +77,11 @@ 继承边距 + + Solution Events + Solution Events + + Missing import discovery 缺少导入发现 @@ -137,6 +142,11 @@ 任务列表 + + Unit testing search + Unit testing search + + Unused reference analysis 未使用的引用分析 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf index 0704b0b7f41ad..67229281990d3 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf @@ -77,6 +77,11 @@ 繼承邊界 + + Solution Events + Solution Events + + Missing import discovery 缺少匯入探索 @@ -137,6 +142,11 @@ 工作清單 + + Unit testing search + Unit testing search + + Unused reference analysis 未使用的參考分析 diff --git a/src/Workspaces/Remote/ServiceHub/ExternalAccess/UnitTesting/Api/UnitTestingBrokeredServiceImplementation.cs b/src/Workspaces/Remote/ServiceHub/ExternalAccess/UnitTesting/Api/UnitTestingBrokeredServiceImplementation.cs index b986ccc46e61c..09ebebacd587b 100644 --- a/src/Workspaces/Remote/ServiceHub/ExternalAccess/UnitTesting/Api/UnitTestingBrokeredServiceImplementation.cs +++ b/src/Workspaces/Remote/ServiceHub/ExternalAccess/UnitTesting/Api/UnitTestingBrokeredServiceImplementation.cs @@ -21,6 +21,12 @@ public static ValueTask RunServiceAsync(Func imple public static UnitTestingIncrementalAnalyzerProvider? TryRegisterAnalyzerProvider(string analyzerName, IUnitTestingIncrementalAnalyzerProviderImplementation provider) => UnitTestingIncrementalAnalyzerProvider.TryRegister(RemoteWorkspaceManager.Default.GetWorkspace(), analyzerName, provider); + public static NewUnitTestingIncrementalAnalyzerProvider? TryRegisterNewAnalyzerProvider(string analyzerName, INewUnitTestingIncrementalAnalyzerProviderImplementation provider) + { + var workspace = RemoteWorkspaceManager.Default.GetWorkspace(); + return NewUnitTestingIncrementalAnalyzerProvider.TryRegister(workspace.Kind, workspace.Services.SolutionServices, analyzerName, provider); + } + [Obsolete("Use RunServiceAsync (that is passsed a Solution) instead", error: false)] public static ValueTask GetSolutionAsync(this UnitTestingPinnedSolutionInfoWrapper solutionInfo, ServiceBrokerClient client, CancellationToken cancellationToken) => RemoteWorkspaceManager.Default.GetSolutionAsync(client, solutionInfo.UnderlyingObject, cancellationToken); diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.PinnedSolution.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.PinnedSolution.cs new file mode 100644 index 0000000000000..80b75eef6ade1 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.PinnedSolution.cs @@ -0,0 +1,42 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed partial class RemoteWorkspace + { + /// + /// Corresponds to a solution that is being used in the remote workspace and has been 'pinned' so that it will + /// not be released. While pinned any concurrent calls into the remote workspace for the same solution checksum + /// will get the same solution instance. Note: services should almost always use + /// instead of calling . The + /// former ensures that the ref-counts around the pinned solution are properly handled. If is used, great care must be + /// followed to ensure it is properly disposed so that data is not held around indefinitely. + /// + public sealed class PinnedSolution : IAsyncDisposable + { + private readonly RemoteWorkspace _workspace; + private readonly InFlightSolution _inFlightSolution; + public readonly Solution Solution; + + public PinnedSolution(RemoteWorkspace workspace, InFlightSolution inFlightSolution, Solution solution) + { + _workspace = workspace; + _inFlightSolution = inFlightSolution; + Solution = solution; + } + + public async ValueTask DisposeAsync() + { + await _workspace.DecrementInFlightCountAsync(_inFlightSolution).ConfigureAwait(false); + } + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index db2b2aa1e35ce..5b3e6041b56b7 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -99,7 +99,7 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, Ca } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -347,6 +347,11 @@ private async Task UpdateProjectInfoAsync(Project project, Checksum inf 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; } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index 777735eba357b..1a2527e5ef894 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; -using static Microsoft.VisualStudio.Threading.ThreadingTools; namespace Microsoft.CodeAnalysis.Remote { @@ -99,6 +98,14 @@ await RunWithSolutionAsync( return RunWithSolutionAsync(assetProvider, solutionChecksum, workspaceVersion: -1, updatePrimaryBranch: false, implementation, cancellationToken); } + public ValueTask GetPinnedSolutionAsync( + AssetProvider assetProvider, + Checksum solutionChecksum, + CancellationToken cancellationToken) + { + return GetPinnedSolutionAsync(assetProvider, solutionChecksum, workspaceVersion: -1, updatePrimaryBranch: false, cancellationToken); + } + private async ValueTask<(Solution solution, T result)> RunWithSolutionAsync( AssetProvider assetProvider, Checksum solutionChecksum, @@ -106,6 +113,27 @@ await RunWithSolutionAsync( bool updatePrimaryBranch, Func> implementation, CancellationToken cancellationToken) + { + try + { + var pinnedSolution = await GetPinnedSolutionAsync(assetProvider, solutionChecksum, workspaceVersion, updatePrimaryBranch, cancellationToken).ConfigureAwait(false); + await using (pinnedSolution.ConfigureAwait(false)) + return (pinnedSolution.Solution, await implementation(pinnedSolution.Solution).ConfigureAwait(false)); + } + catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken, ErrorSeverity.Critical)) + { + // Any non-cancellation exception is bad and needs to be reported. We will still ensure that we cleanup + // below though no matter what happens so that other calls to OOP can properly work. + throw ExceptionUtilities.Unreachable(); + } + } + + private async ValueTask GetPinnedSolutionAsync( + AssetProvider assetProvider, + Checksum solutionChecksum, + int workspaceVersion, + bool updatePrimaryBranch, + CancellationToken cancellationToken) { Contract.ThrowIfNull(solutionChecksum); Contract.ThrowIfTrue(solutionChecksum == Checksum.Null); @@ -114,19 +142,21 @@ await RunWithSolutionAsync( // increment the in-flight of that solution until we decrement it at the end of our try/finally block. var (inFlightSolution, solutionTask) = await AcquireSolutionAndIncrementInFlightCountAsync().ConfigureAwait(false); + Exception? exception = null; try { - return await ProcessSolutionAsync(inFlightSolution, solutionTask).ConfigureAwait(false); + var solution = await GetSolutionAsync(inFlightSolution, solutionTask).ConfigureAwait(false); + return new PinnedSolution(this, inFlightSolution, solution); } - catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken, ErrorSeverity.Critical)) + catch (Exception ex) when ((exception = ex) == null) { - // Any non-cancellation exception is bad and needs to be reported. We will still ensure that we cleanup - // below though no matter what happens so that other calls to OOP can properly work. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } finally { - await DecrementInFlightCountAsync(inFlightSolution).ConfigureAwait(false); + // Ensure we cleanup if an exception occurred. Otherwise, we'll never release this data. + if (exception != null) + await DecrementInFlightCountAsync(inFlightSolution).ConfigureAwait(false); } // Gets or creates a solution corresponding to the requested checksum. This will always succeed, and will @@ -150,12 +180,12 @@ await RunWithSolutionAsync( { // Any exception thrown in the above (including cancellation) is critical and unrecoverable. We // will have potentially started work, while also leaving ourselves in some inconsistent state. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } - async ValueTask<(Solution solution, T result)> ProcessSolutionAsync(InFlightSolution inFlightSolution, Task solutionTask) + async ValueTask GetSolutionAsync(InFlightSolution inFlightSolution, Task solutionTask) { // We must have at least 1 for the in-flight-count (representing this current in-flight call). Contract.ThrowIfTrue(inFlightSolution.InFlightCount < 1); @@ -174,48 +204,44 @@ await RunWithSolutionAsync( _lastRequestedAnyBranchSolution = (solutionChecksum, solution); } - // Now, pass it to the callback to do the work. Any other callers into us will be able to benefit from - // using this same solution as well - var result = await implementation(solution).ConfigureAwait(false); - - return (solution, result); + return solution; } + } - async ValueTask DecrementInFlightCountAsync(InFlightSolution inFlightSolution) - { - // All this work is intentionally not cancellable. We must do the decrement to ensure our cache state - // is consistent. This will block the calling thread. However, this should only be for a short amount - // of time as nothing in RemoteWorkspace should ever hold this lock for long periods of time. + private async ValueTask DecrementInFlightCountAsync(InFlightSolution inFlightSolution) + { + // All this work is intentionally not cancellable. We must do the decrement to ensure our cache state + // is consistent. This will block the calling thread. However, this should only be for a short amount + // of time as nothing in RemoteWorkspace should ever hold this lock for long periods of time. - try + try + { + ImmutableArray solutionComputationTasks; + using (await _gate.DisposableWaitAsync(CancellationToken.None).ConfigureAwait(false)) { - ImmutableArray solutionComputationTasks; - using (await _gate.DisposableWaitAsync(CancellationToken.None).ConfigureAwait(false)) - { - - // finally, decrement our in-flight-count on the solution. If we were the last one keeping it alive, it - // will get removed from our caches. - solutionComputationTasks = inFlightSolution.DecrementInFlightCount_NoLock(); - } - // If we were the request that decremented the in-flight-count to 0, then ensure we wait for all the - // solution-computation tasks to finish. If we do not do this then it's possible for this call to - // return all the way back to the host side unpinning the solution we have pinned there. This may - // happen concurrently with the solution-computation calls calling back into the host which will - // then crash due to that solution no longer being pinned there. While this does force this caller - // to wait for those tasks to stop, this should ideally be fast as they will have been cancelled - // when the in-flight-count went to 0. - // - // Use a NoThrowAwaitable as we want to await all tasks here regardless of how individual ones may cancel. - foreach (var task in solutionComputationTasks) - await task.NoThrowAwaitable(false); - } - catch (Exception ex) when (FatalError.ReportAndPropagate(ex, ErrorSeverity.Critical)) - { - // Similar to AcquireSolutionAndIncrementInFlightCountAsync Any exception thrown in the above - // (including cancellation) is critical and unrecoverable. We must clean up our state, and anything - // that prevents that could leave us in an inconsistent position. + // finally, decrement our in-flight-count on the solution. If we were the last one keeping it alive, it + // will get removed from our caches. + solutionComputationTasks = inFlightSolution.DecrementInFlightCount_NoLock(); } + + // If we were the request that decremented the in-flight-count to 0, then ensure we wait for all the + // solution-computation tasks to finish. If we do not do this then it's possible for this call to + // return all the way back to the host side unpinning the solution we have pinned there. This may + // happen concurrently with the solution-computation calls calling back into the host which will + // then crash due to that solution no longer being pinned there. While this does force this caller + // to wait for those tasks to stop, this should ideally be fast as they will have been cancelled + // when the in-flight-count went to 0. + // + // Use a NoThrowAwaitable as we want to await all tasks here regardless of how individual ones may cancel. + foreach (var task in solutionComputationTasks) + await task.NoThrowAwaitable(false); + } + catch (Exception ex) when (FatalError.ReportAndPropagate(ex, ErrorSeverity.Critical)) + { + // Similar to AcquireSolutionAndIncrementInFlightCountAsync Any exception thrown in the above + // (including cancellation) is critical and unrecoverable. We must clean up our state, and anything + // that prevents that could leave us in an inconsistent position. } } @@ -262,7 +288,7 @@ private async Task ComputeDisconnectedSolutionAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs index 8565fa9469b77..a06d5a2962ebb 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs @@ -77,7 +77,7 @@ public Task CreateAsync( } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -122,7 +122,7 @@ static FactoryBase() protected abstract TService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback); protected sealed override TService CreateService(in ServiceConstructionArguments arguments) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); protected sealed override TService CreateService( in ServiceConstructionArguments arguments, diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs index 407a0c31938fb..5f068842536d0 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.Runtime; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -98,6 +100,24 @@ protected async ValueTask RunWithSolutionAsync( return result; } + protected async IAsyncEnumerable StreamWithSolutionAsync( + Checksum solutionChecksum, + Func> implementation, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + var workspace = GetWorkspace(); + var assetProvider = workspace.CreateAssetProvider(solutionChecksum, WorkspaceManager.SolutionAssetCache, SolutionAssetSource); + + // Ensure the solution stays pinned while we're streaming results back. + var pinnedSolution = await workspace.GetPinnedSolutionAsync( + assetProvider, solutionChecksum, cancellationToken).ConfigureAwait(false); + await using (pinnedSolution.ConfigureAwait(false)) + { + await foreach (var item in implementation(pinnedSolution.Solution, cancellationToken).ConfigureAwait(false)) + yield return item; + } + } + protected ValueTask RunServiceAsync(Func> implementation, CancellationToken cancellationToken) { WorkspaceManager.SolutionAssetCache.UpdateLastActivityTime(); @@ -119,7 +139,7 @@ internal static async ValueTask RunServiceImplAsync(Func implementation, + CancellationToken cancellationToken) + { + return RunServiceAsync( + solutionChecksum1, + s1 => RunServiceAsync( + solutionChecksum2, + s2 => implementation(s1, s2), + cancellationToken), + cancellationToken); + } + internal static async ValueTask RunServiceImplAsync(Func implementation, CancellationToken cancellationToken) { try @@ -154,7 +189,7 @@ internal static async ValueTask RunServiceImplAsync(Func ConvertToStructAsync( return (docId, renamedToken.Value.Span); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static async Task CleanupAsync(Solution oldSolution, Solution newSolution, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs index 0d878efb54d66..5c3bb62686c99 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs @@ -2,62 +2,55 @@ // 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.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DesignerAttribute; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; +using StreamJsonRpc; namespace Microsoft.CodeAnalysis.Remote { internal sealed class RemoteDesignerAttributeDiscoveryService : BrokeredServiceBase, IRemoteDesignerAttributeDiscoveryService { - private sealed class CallbackWrapper : IDesignerAttributeDiscoveryService.ICallback + /// + /// Allow designer attribute computation to continue on on the server, even while the client is processing the + /// last batch of results. + /// + /// + /// This value was not determined empirically. + /// + private const int MaxReadAhead = 64; + + internal sealed class Factory : FactoryBase { - private readonly RemoteCallback _callback; - private readonly RemoteServiceCallbackId _callbackId; - - public CallbackWrapper( - RemoteCallback callback, - RemoteServiceCallbackId callbackId) - { - _callback = callback; - _callbackId = callbackId; - } - - public ValueTask ReportDesignerAttributeDataAsync(ImmutableArray data, CancellationToken cancellationToken) - => _callback.InvokeAsync((callback, cancellationToken) => callback.ReportDesignerAttributeDataAsync(_callbackId, data, cancellationToken), cancellationToken); - } - - internal sealed class Factory : FactoryBase - { - protected override IRemoteDesignerAttributeDiscoveryService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) - => new RemoteDesignerAttributeDiscoveryService(arguments, callback); + protected override IRemoteDesignerAttributeDiscoveryService CreateService(in ServiceConstructionArguments arguments) + => new RemoteDesignerAttributeDiscoveryService(arguments); } - private readonly RemoteCallback _callback; - - public RemoteDesignerAttributeDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) + public RemoteDesignerAttributeDiscoveryService(in ServiceConstructionArguments arguments) : base(arguments) { - _callback = callback; } - public ValueTask DiscoverDesignerAttributesAsync( - RemoteServiceCallbackId callbackId, + public IAsyncEnumerable DiscoverDesignerAttributesAsync( Checksum solutionChecksum, + ProjectId projectId, DocumentId? priorityDocument, CancellationToken cancellationToken) { - return RunServiceAsync( + var stream = StreamWithSolutionAsync( solutionChecksum, - solution => + (solution, cancellationToken) => { + var project = solution.GetRequiredProject(projectId); var service = solution.Services.GetRequiredService(); - return service.ProcessSolutionAsync( - solution, priorityDocument, new CallbackWrapper(_callback, callbackId), cancellationToken); - }, - cancellationToken); + return service.ProcessProjectAsync(project, priorityDocument, cancellationToken); + }, cancellationToken); + return stream.WithJsonRpcSettings(new JsonRpcEnumerableSettings { MaxReadAhead = MaxReadAhead }); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index 089c784d3f18e..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); } @@ -117,7 +134,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che } catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } }, 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); - } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/InheritanceMargin/RemoteInheritanceMarginService.cs b/src/Workspaces/Remote/ServiceHub/Services/InheritanceMargin/RemoteInheritanceMarginService.cs index 2b1b8f34d4ed4..6cceb3a60a897 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/InheritanceMargin/RemoteInheritanceMarginService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/InheritanceMargin/RemoteInheritanceMarginService.cs @@ -33,11 +33,11 @@ public ValueTask> GetInheritanceMarginItem bool frozenPartialSemantics, CancellationToken cancellationToken) { - return RunServiceAsync(solutionChecksum, solution => + return RunServiceAsync(solutionChecksum, async solution => { - var document = solution.GetRequiredDocument(documentId); + var document = await solution.GetRequiredDocumentAsync(documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); var service = document.GetRequiredLanguageService(); - return service.GetInheritanceMemberItemsAsync(document, spanToSearch, includeGlobalImports, frozenPartialSemantics, cancellationToken); + return await service.GetInheritanceMemberItemsAsync(document, spanToSearch, includeGlobalImports, frozenPartialSemantics, cancellationToken).ConfigureAwait(false); }, cancellationToken); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs b/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs new file mode 100644 index 0000000000000..8c330c8e48538 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs @@ -0,0 +1,57 @@ +// 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.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.LegacySolutionEvents; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteLegacySolutionEventsAggregationService : BrokeredServiceBase, IRemoteLegacySolutionEventsAggregationService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteLegacySolutionEventsAggregationService CreateService(in ServiceConstructionArguments arguments) + => new RemoteLegacySolutionEventsAggregationService(arguments); + } + + public RemoteLegacySolutionEventsAggregationService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask ShouldReportChangesAsync(CancellationToken cancellationToken) + { + return RunServiceImplAsync( + cancellationToken => + { + var services = this.GetWorkspaceServices(); + var aggregationService = services.GetRequiredService(); + return new ValueTask(aggregationService.ShouldReportChanges(services)); + }, + cancellationToken); + } + + public ValueTask OnWorkspaceChangedAsync( + Checksum oldSolutionChecksum, + Checksum newSolutionChecksum, + WorkspaceChangeKind kind, + ProjectId? projectId, + DocumentId? documentId, + CancellationToken cancellationToken) + { + return RunServiceAsync(oldSolutionChecksum, newSolutionChecksum, + async (oldSolution, newSolution) => + { + var aggregationService = oldSolution.Services.GetRequiredService(); + await aggregationService.OnWorkspaceChangedAsync( + new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId), cancellationToken).ConfigureAwait(false); + }, cancellationToken); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs index 3301729bb5554..ee20abdd7faa8 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs @@ -3,39 +3,43 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; +using StreamJsonRpc; namespace Microsoft.CodeAnalysis.Remote { internal sealed class RemoteNavigateToSearchService : BrokeredServiceBase, IRemoteNavigateToSearchService { - internal sealed class Factory : FactoryBase - { - protected override IRemoteNavigateToSearchService CreateService( - in ServiceConstructionArguments arguments, RemoteCallback callback) - => new RemoteNavigateToSearchService(arguments, callback); - } - - private readonly RemoteCallback _callback; + /// + /// Navigate to is implemented using . This API works by having the client + /// "pull" for results from the server. That means that the computation to produce the next result happens only + /// when the client is actually asking for that. We would like to utilize resources more thoroughly when + /// searching, allowing for searching for results on multiple cores. As such, we set a "max read ahead" amount + /// that allows the server to keep processing and producing results, even as the client is processing the batch + /// of results. + /// + /// + /// This value was not determined empirically. + /// + private const int MaxReadAhead = 64; - public RemoteNavigateToSearchService(in ServiceConstructionArguments arguments, RemoteCallback callback) - : base(arguments) + internal sealed class Factory : FactoryBase { - _callback = callback; + protected override IRemoteNavigateToSearchService CreateService(in ServiceConstructionArguments arguments) + => new RemoteNavigateToSearchService(arguments); } - private Func GetCallback( - RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) + public RemoteNavigateToSearchService(in ServiceConstructionArguments arguments) + : base(arguments) { - return async i => await _callback.InvokeAsync((callback, c) => - callback.OnResultFoundAsync(callbackId, i), - cancellationToken).ConfigureAwait(false); } public ValueTask HydrateAsync(Checksum solutionChecksum, CancellationToken cancellationToken) @@ -47,75 +51,80 @@ public ValueTask HydrateAsync(Checksum solutionChecksum, CancellationToken cance return RunServiceAsync(solutionChecksum, solution => ValueTaskFactory.CompletedTask, cancellationToken); } - public ValueTask SearchDocumentAsync( + public IAsyncEnumerable SearchDocumentAsync( Checksum solutionChecksum, DocumentId documentId, string searchPattern, ImmutableArray kinds, - RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) { - return RunServiceAsync(solutionChecksum, async solution => - { - var document = solution.GetRequiredDocument(documentId); - var callback = GetCallback(callbackId, cancellationToken); + return StreamWithSolutionAsync( + solutionChecksum, + (solution, cancellationToken) => + { + var document = solution.GetRequiredDocument(documentId); - await AbstractNavigateToSearchService.SearchDocumentInCurrentProcessAsync( - document, searchPattern, kinds.ToImmutableHashSet(), callback, cancellationToken).ConfigureAwait(false); - }, cancellationToken); + return AbstractNavigateToSearchService.SearchDocumentInCurrentProcessAsync( + document, searchPattern, kinds.ToImmutableHashSet(), cancellationToken); + }, + cancellationToken).WithJsonRpcSettings(new JsonRpcEnumerableSettings { MaxReadAhead = MaxReadAhead }); } - public ValueTask SearchProjectAsync( + public IAsyncEnumerable SearchProjectAsync( Checksum solutionChecksum, ProjectId projectId, ImmutableArray priorityDocumentIds, string searchPattern, ImmutableArray kinds, - RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) { - return RunServiceAsync(solutionChecksum, async solution => - { - var project = solution.GetRequiredProject(projectId); - var callback = GetCallback(callbackId, cancellationToken); + return StreamWithSolutionAsync( + solutionChecksum, + (solution, cancellationToken) => + { + var project = solution.GetRequiredProject(projectId); - var priorityDocuments = priorityDocumentIds.SelectAsArray(d => solution.GetRequiredDocument(d)); + var priorityDocuments = priorityDocumentIds.SelectAsArray(d => solution.GetRequiredDocument(d)); - await AbstractNavigateToSearchService.SearchProjectInCurrentProcessAsync( - project, priorityDocuments, searchPattern, kinds.ToImmutableHashSet(), callback, cancellationToken).ConfigureAwait(false); - }, cancellationToken); + return AbstractNavigateToSearchService.SearchProjectInCurrentProcessAsync( + project, priorityDocuments, searchPattern, kinds.ToImmutableHashSet(), cancellationToken); + }, cancellationToken).WithJsonRpcSettings(new JsonRpcEnumerableSettings { MaxReadAhead = MaxReadAhead }); } - public ValueTask SearchGeneratedDocumentsAsync( + public IAsyncEnumerable SearchGeneratedDocumentsAsync( Checksum solutionChecksum, ProjectId projectId, string searchPattern, ImmutableArray kinds, - RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) { - return RunServiceAsync(solutionChecksum, async solution => - { - var project = solution.GetRequiredProject(projectId); - var callback = GetCallback(callbackId, cancellationToken); + return StreamWithSolutionAsync( + solutionChecksum, + (solution, cancellationToken) => + { + var project = solution.GetRequiredProject(projectId); - await AbstractNavigateToSearchService.SearchGeneratedDocumentsInCurrentProcessAsync( - project, searchPattern, kinds.ToImmutableHashSet(), callback, cancellationToken).ConfigureAwait(false); - }, cancellationToken); + return AbstractNavigateToSearchService.SearchGeneratedDocumentsInCurrentProcessAsync( + project, searchPattern, kinds.ToImmutableHashSet(), cancellationToken); + }, + cancellationToken).WithJsonRpcSettings(new JsonRpcEnumerableSettings { MaxReadAhead = MaxReadAhead }); } - public ValueTask SearchCachedDocumentsAsync(ImmutableArray documentKeys, ImmutableArray priorityDocumentKeys, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken) + public IAsyncEnumerable SearchCachedDocumentsAsync( + ImmutableArray documentKeys, + ImmutableArray priorityDocumentKeys, + string searchPattern, + ImmutableArray kinds, + CancellationToken cancellationToken) { - return RunServiceAsync(async cancellationToken => - { - // Intentionally do not call GetSolutionAsync here. We do not want the cost of - // synchronizing the solution over to the remote side. Instead, we just directly - // check whatever cached data we have from the previous vs session. - var callback = GetCallback(callbackId, cancellationToken); - var storageService = GetWorkspaceServices().GetPersistentStorageService(); - await AbstractNavigateToSearchService.SearchCachedDocumentsInCurrentProcessAsync( - storageService, documentKeys, priorityDocumentKeys, searchPattern, kinds.ToImmutableHashSet(), callback, cancellationToken).ConfigureAwait(false); - }, cancellationToken); + WorkspaceManager.SolutionAssetCache.UpdateLastActivityTime(); + + // Intentionally do not call GetSolutionAsync here. We do not want the cost of + // synchronizing the solution over to the remote side. Instead, we just directly + // check whatever cached data we have from the previous VS session. + var storageService = GetWorkspaceServices().GetPersistentStorageService(); + return AbstractNavigateToSearchService.SearchCachedDocumentsInCurrentProcessAsync( + storageService, documentKeys, priorityDocumentKeys, searchPattern, kinds.ToImmutableHashSet(), cancellationToken); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs index 369c74f6f668a..5d38cc493c2ab 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs @@ -172,50 +172,6 @@ public ValueTask> FindProjectSour }, cancellationToken); } - public ValueTask AnalyzeDocumentAsync( - Checksum solutionChecksum, - DocumentId documentId, - bool isMethodBodyEdit, - CancellationToken cancellationToken) - { - return RunServiceAsync( - solutionChecksum, - async solution => - { - var cacheService = solution.Services.GetRequiredService(); - var document = await solution.GetRequiredDocumentAsync(documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); - await cacheService.AnalyzeDocumentAsync(document, isMethodBodyEdit, cancellationToken).ConfigureAwait(false); - }, - cancellationToken); - } - - public ValueTask AnalyzeProjectAsync( - Checksum solutionChecksum, - ProjectId projectId, - CancellationToken cancellationToken) - { - return RunServiceAsync( - solutionChecksum, - async solution => - { - var cacheService = solution.Services.GetRequiredService(); - await cacheService.AnalyzeProjectAsync(solution.GetRequiredProject(projectId), cancellationToken).ConfigureAwait(false); - }, - cancellationToken); - } - - public ValueTask RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) - { - return RunServiceAsync( - cancellationToken => - { - var cacheService = GetWorkspaceServices().GetRequiredService(); - cacheService.RemoveProject(projectId); - return default; - }, - cancellationToken); - } - private sealed class FindLiteralReferencesProgressCallback : IStreamingFindLiteralReferencesProgress, IStreamingProgressTracker { private readonly RemoteCallback _callback; diff --git a/src/Workspaces/Remote/ServiceHub/Services/UnitTesting/RemoteUnitTestingSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/UnitTesting/RemoteUnitTestingSearchService.cs new file mode 100644 index 0000000000000..605bb9c1ee4ac --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/UnitTesting/RemoteUnitTestingSearchService.cs @@ -0,0 +1,45 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting; +using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteUnitTestingSearchService : BrokeredServiceBase, IRemoteUnitTestingSearchService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteUnitTestingSearchService CreateService(in ServiceConstructionArguments arguments) + => new RemoteUnitTestingSearchService(arguments); + } + + public RemoteUnitTestingSearchService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask> GetSourceLocationsAsync( + Checksum solutionChecksum, + ProjectId projectId, + UnitTestingSearchQuery query, + CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, async solution => + { + var project = solution.GetRequiredProject(projectId); + + var results = await UnitTestingSearchHelpers.GetSourceLocationsAsync(project, query, cancellationToken).ConfigureAwait(false); + + return results.SelectAsArray(r => new UnitTestingSourceLocation( + new DocumentIdSpan(r.DocumentSpan.Document.Id, r.DocumentSpan.SourceSpan), + r.Span)); + }, cancellationToken); + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs index bda8f5eb4af17..a805162e52e06 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs @@ -136,7 +136,7 @@ private static string GetPreferBracesPreferenceEditorConfigString(CodeStyleOptio PreferBracesPreference.None => $"false{notificationString}", PreferBracesPreference.WhenMultiline => $"when_multiline{notificationString}", PreferBracesPreference.Always => $"true{notificationString}", - _ => throw ExceptionUtilities.Unreachable, + _ => throw ExceptionUtilities.Unreachable(), }; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 17841b717915b..91cf9885411b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -452,7 +452,7 @@ private static bool RemovalChangesAssociation( return false; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private static bool IsAssociative(SyntaxKind kind) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs index 8ce5f4750b683..f4c84347068e2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs @@ -18,9 +18,7 @@ public static ISet GetPrecedingModifiers(this SyntaxTree syntaxTree, => syntaxTree.GetPrecedingModifiers(position, cancellationToken, out _); public static ISet GetPrecedingModifiers( -#pragma warning disable IDE0060 // Remove unused parameter - Unused this parameter for consistency with other extension methods. this SyntaxTree syntaxTree, -#pragma warning restore IDE0060 // Remove unused parameter int position, CancellationToken cancellationToken, out int positionBeforeModifiers) @@ -52,6 +50,7 @@ public static ISet GetPrecedingModifiers( case SyntaxKind.RefKeyword: case SyntaxKind.OutKeyword: case SyntaxKind.InKeyword: + case SyntaxKind.RequiredKeyword: result.Add(token.Kind()); positionBeforeModifiers = token.FullSpan.Start; token = token.GetPreviousToken(includeSkipped: true); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs index 6b858eb9a44b5..0d52ec108811c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs @@ -59,7 +59,7 @@ protected override SyntaxTrivia CreateEndOfLine() return _newLine; } - protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2) + protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2, CancellationToken cancellationToken) { if (IsStartOrEndOfFile(trivia1, trivia2)) { @@ -99,6 +99,40 @@ protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, if (trivia2.IsKind(SyntaxKind.RegionDirectiveTrivia) || trivia2.IsKind(SyntaxKind.EndRegionDirectiveTrivia)) { + // When we have a '#region' in conditionally disabled conditional (e.g, `#if false`), we cannot determine a correct indentation for '#region'. + // So we preserve the existing indentation. + // To figure whether we are in a disabled region, we do the following: + // - Starting from the given trivia, keep going back. + // - Once we find a disabled text, we know this is a disabled region. + // - If we find a BranchingDirectiveTriviaSyntax, we can directly determine whether it's active or not via BranchTaken property. + var previous = trivia2; + while ((previous = previous.GetPreviousTrivia(previous.SyntaxTree, cancellationToken)) != default) + { + if (previous.IsKind(SyntaxKind.DisabledTextTrivia)) + { + return LineColumnRule.Preserve; + } + else if (previous.IsKind(SyntaxKind.EndIfDirectiveTrivia)) + { + // To correctly determine if we are in a disabled region or not, we'll have to ignore + // everything until the corresponding #if (keeping in mind nested `#if` conditionals). + // Then, continue from there. + // For now, we don't do that and assume we are in active region. + break; + } + else if (previous.HasStructure && previous.GetStructure() is BranchingDirectiveTriviaSyntax branchingDirectiveTrivia) + { + if (!branchingDirectiveTrivia.BranchTaken) + { + return LineColumnRule.Preserve; + } + else + { + break; + } + } + } + return LineColumnRule.PreserveLinesWithDefaultIndentation(lines); } @@ -359,7 +393,7 @@ protected override bool LineContinuationFollowedByWhitespaceComment(SyntaxTrivia /// protected override bool IsVisualBasicComment(SyntaxTrivia trivia) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs index 5cf4d4b8b07f4..7f91948c28d38 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs @@ -218,7 +218,7 @@ private static bool OnSkippedTokensOrText(SyntaxTrivia trivia) return false; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private bool OnRegion(SyntaxTrivia trivia, int currentIndex) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 8e895bd07ad85..66460b40e6566 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -257,11 +257,11 @@ public IEnumerable GetDeclaredSymbols( } } - public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, CancellationToken cancellationToken) - => ((ArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams: false, cancellationToken); + public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + => ((ArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams, cancellationToken); - public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, CancellationToken cancellationToken) - => ((AttributeArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams: false, cancellationToken); + public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + => ((AttributeArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams, cancellationToken); // Normal arguments can't reference fields/properties in c# public ISymbol FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 1a40133a78233..ca8cc94433236 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -618,7 +618,7 @@ public bool IsAttributeNamedArgumentIdentifier([NotNullWhen(true)] SyntaxNode? n } public SyntaxNode? GetContainingVariableDeclaratorOfFieldDeclaration(SyntaxNode? node) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public bool IsNameOfSubpattern([NotNullWhen(true)] SyntaxNode? node) => node.IsKind(SyntaxKind.IdentifierName) && @@ -1497,6 +1497,13 @@ public void GetPartsOfUnaryPattern(SyntaxNode node, out SyntaxToken operatorToke pattern = unaryPattern.Pattern; } + public void GetPartsOfRelationalPattern(SyntaxNode node, out SyntaxToken operatorToken, out SyntaxNode expression) + { + var relationalPattern = (RelationalPatternSyntax)node; + operatorToken = relationalPattern.OperatorToken; + expression = relationalPattern.Expression; + } + public SyntaxNode GetTypeOfTypePattern(SyntaxNode node) => ((TypePatternSyntax)node).Type; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 8a1c40805b0f0..fc2ef37626e3b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -100,6 +100,7 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int? OrPattern => (int)SyntaxKind.OrPattern; public int? ParenthesizedPattern => (int)SyntaxKind.ParenthesizedPattern; public int? RecursivePattern => (int)SyntaxKind.RecursivePattern; + public int? RelationalPattern => (int)SyntaxKind.RelationalPattern; public int? TypePattern => (int)SyntaxKind.TypePattern; public int? VarPattern => (int)SyntaxKind.VarPattern; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs index d1f203cf55f6b..09e5d2d6ff056 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs @@ -20,18 +20,20 @@ namespace Microsoft.CodeAnalysis.CodeCleanup; [DataContract] -internal sealed record class CodeCleanupOptions( - [property: DataMember] SyntaxFormattingOptions FormattingOptions, - [property: DataMember] SimplifierOptions SimplifierOptions) +internal sealed record class CodeCleanupOptions { + [DataMember] public required SyntaxFormattingOptions FormattingOptions { get; init; } + [DataMember] public required SimplifierOptions SimplifierOptions { get; init; } [DataMember] public AddImportPlacementOptions AddImportOptions { get; init; } = AddImportPlacementOptions.Default; [DataMember] public DocumentFormattingOptions DocumentFormattingOptions { get; init; } = DocumentFormattingOptions.Default; #if !CODE_STYLE public static CodeCleanupOptions GetDefault(LanguageServices languageServices) - => new( - FormattingOptions: SyntaxFormattingOptions.GetDefault(languageServices), - SimplifierOptions: SimplifierOptions.GetDefault(languageServices)); + => new() + { + FormattingOptions = SyntaxFormattingOptions.GetDefault(languageServices), + SimplifierOptions = SimplifierOptions.GetDefault(languageServices) + }; public OrganizeImportsOptions GetOrganizeImportsOptions() => new() @@ -83,18 +85,13 @@ internal static class CodeCleanupOptionsProviders { #if !CODE_STYLE public static CodeCleanupOptions GetCodeCleanupOptions(this AnalyzerConfigOptions options, bool allowImportsInHiddenRegions, CodeCleanupOptions? fallbackOptions, LanguageServices languageServices) - { - var formattingOptions = options.GetSyntaxFormattingOptions(fallbackOptions?.FormattingOptions, languageServices); - var simplifierOptions = options.GetSimplifierOptions(fallbackOptions?.SimplifierOptions, languageServices); - var addImportOptions = options.GetAddImportPlacementOptions(allowImportsInHiddenRegions, fallbackOptions?.AddImportOptions, languageServices); - var documentFormattingOptions = options.GetDocumentFormattingOptions(fallbackOptions?.DocumentFormattingOptions); - - return new CodeCleanupOptions(formattingOptions, simplifierOptions) + => new() { - AddImportOptions = addImportOptions, - DocumentFormattingOptions = documentFormattingOptions + FormattingOptions = options.GetSyntaxFormattingOptions(fallbackOptions?.FormattingOptions, languageServices), + SimplifierOptions = options.GetSimplifierOptions(fallbackOptions?.SimplifierOptions, languageServices), + AddImportOptions = options.GetAddImportPlacementOptions(allowImportsInHiddenRegions, fallbackOptions?.AddImportOptions, languageServices), + DocumentFormattingOptions = options.GetDocumentFormattingOptions(fallbackOptions?.DocumentFormattingOptions), }; - } public static async ValueTask GetCodeCleanupOptionsAsync(this Document document, CodeCleanupOptions? fallbackOptions, CancellationToken cancellationToken) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CleanCodeGenerationOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CleanCodeGenerationOptions.cs index dc99021324b8d..9cc7fb1756363 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CleanCodeGenerationOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CleanCodeGenerationOptions.cs @@ -12,17 +12,28 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; [DataContract] -internal readonly record struct CleanCodeGenerationOptions( - [property: DataMember(Order = 0)] CodeGenerationOptions GenerationOptions, - [property: DataMember(Order = 1)] CodeCleanupOptions CleanupOptions) +internal readonly record struct CleanCodeGenerationOptions { + [DataMember] + public required CodeGenerationOptions GenerationOptions { get; init; } + + [DataMember] + public required CodeCleanupOptions CleanupOptions { get; init; } + #if !CODE_STYLE public static CleanCodeGenerationOptions GetDefault(LanguageServices languageServices) - => new(CodeGenerationOptions.GetDefault(languageServices), - CodeCleanupOptions.GetDefault(languageServices)); + => new() + { + GenerationOptions = CodeGenerationOptions.GetDefault(languageServices), + CleanupOptions = CodeCleanupOptions.GetDefault(languageServices) + }; public CodeAndImportGenerationOptions CodeAndImportGenerationOptions - => new(GenerationOptions, CleanupOptions.AddImportOptions); + => new() + { + GenerationOptions = GenerationOptions, + AddImportOptions = CleanupOptions.AddImportOptions + }; #endif } @@ -60,9 +71,11 @@ async ValueTask OptionsProvider. internal static class CleanCodeGenerationOptionsProviders { public static async ValueTask GetCleanCodeGenerationOptionsAsync(this Document document, CleanCodeGenerationOptions fallbackOptions, CancellationToken cancellationToken) - => new( - await document.GetCodeGenerationOptionsAsync(fallbackOptions.GenerationOptions, cancellationToken).ConfigureAwait(false), - await document.GetCodeCleanupOptionsAsync(fallbackOptions.CleanupOptions, cancellationToken).ConfigureAwait(false)); + => new() + { + GenerationOptions = await document.GetCodeGenerationOptionsAsync(fallbackOptions.GenerationOptions, cancellationToken).ConfigureAwait(false), + CleanupOptions = await document.GetCodeCleanupOptionsAsync(fallbackOptions.CleanupOptions, cancellationToken).ConfigureAwait(false) + }; public static async ValueTask GetCleanCodeGenerationOptionsAsync(this Document document, CleanCodeGenerationOptionsProvider fallbackOptionsProvider, CancellationToken cancellationToken) => await document.GetCleanCodeGenerationOptionsAsync(await ((OptionsProvider)fallbackOptionsProvider).GetOptionsAsync(document.Project.Services, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs index b7425e95f2c5e..fad46022618e4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs @@ -52,13 +52,21 @@ public CodeGenerationContextInfo GetInfo(CodeGenerationContext context, Project } [DataContract] -internal readonly record struct CodeAndImportGenerationOptions( - [property: DataMember(Order = 0)] CodeGenerationOptions GenerationOptions, - [property: DataMember(Order = 1)] AddImportPlacementOptions AddImportOptions) +internal readonly record struct CodeAndImportGenerationOptions { + [DataMember] + public required CodeGenerationOptions GenerationOptions { get; init; } + + [DataMember] + public required AddImportPlacementOptions AddImportOptions { get; init; } + #if !CODE_STYLE internal static CodeAndImportGenerationOptions GetDefault(LanguageServices languageServices) - => new(CodeGenerationOptions.GetDefault(languageServices), AddImportPlacementOptions.Default); + => new() + { + GenerationOptions = CodeGenerationOptions.GetDefault(languageServices), + AddImportOptions = AddImportPlacementOptions.Default + }; internal CodeAndImportGenerationOptionsProvider CreateProvider() => new Provider(this); @@ -115,13 +123,13 @@ public static CodeGenerationOptions.CommonOptions GetCommonCodeGenerationOptions } #if !CODE_STYLE - public static CodeGenerationOptions GetCodeGenerationOptions(this AnalyzerConfigOptions options, CodeGenerationOptions? fallbackOptions, HostLanguageServices languageServices) + public static CodeGenerationOptions GetCodeGenerationOptions(this AnalyzerConfigOptions options, CodeGenerationOptions? fallbackOptions, LanguageServices languageServices) => languageServices.GetRequiredService().GetCodeGenerationOptions(options, fallbackOptions); public static async ValueTask GetCodeGenerationOptionsAsync(this Document document, CodeGenerationOptions? fallbackOptions, CancellationToken cancellationToken) { var configOptions = await document.GetAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false); - return configOptions.GetCodeGenerationOptions(fallbackOptions, document.Project.LanguageServices); + return configOptions.GetCodeGenerationOptions(fallbackOptions, document.Project.Services); } public static async ValueTask GetCodeGenerationOptionsAsync(this Document document, CodeGenerationOptionsProvider fallbackOptionsProvider, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.Node.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.Node.cs index 15e61f73d2bf7..f9f3025cf4e0f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.Node.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.Node.cs @@ -54,7 +54,7 @@ internal void SetLeftRight(Node? left, Node? right, in TIntrospec } else { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index eb64e38e29f6f..23e5cb4522017 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -22,6 +22,9 @@ + + + @@ -56,6 +59,7 @@ + @@ -533,7 +537,6 @@ - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs index 0df4081a80ba1..b13a9d1eed73c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs @@ -46,10 +46,10 @@ public static bool TryGetEditorConfigOption(this AnalyzerConfigOptions analyz => TryGetEditorConfigOption(analyzerConfigOptions, option, defaultValue: default, out value); public static T GetEditorConfigOption(this AnalyzerConfigOptions analyzerConfigOptions, TOption option, T defaultValue) - => TryGetEditorConfigOption(analyzerConfigOptions, option, new Optional(defaultValue), out var value) ? value! : throw ExceptionUtilities.Unreachable; + => TryGetEditorConfigOption(analyzerConfigOptions, option, new Optional(defaultValue), out var value) ? value! : throw ExceptionUtilities.Unreachable(); public static T GetEditorConfigOptionValue(this AnalyzerConfigOptions analyzerConfigOptions, TOption option, T defaultValue) - => TryGetEditorConfigOption(analyzerConfigOptions, option, new Optional?>(new CodeStyleOption2(defaultValue, NotificationOption2.None)), out var style) ? style!.Value : throw ExceptionUtilities.Unreachable; + => TryGetEditorConfigOption(analyzerConfigOptions, option, new Optional?>(new CodeStyleOption2(defaultValue, NotificationOption2.None)), out var style) ? style!.Value : throw ExceptionUtilities.Unreachable(); private static bool TryGetEditorConfigOption(this AnalyzerConfigOptions analyzerConfigOptions, TOption option, Optional defaultValue, out T? value) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs index 8a39eac2de3a4..eada350447e61 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs @@ -191,5 +191,14 @@ public static bool IsCompilationEnd(this DiagnosticDescriptor descriptor) internal static Uri? GetValidHelpLinkUri(this DiagnosticDescriptor descriptor) => Uri.TryCreate(descriptor.HelpLinkUri, UriKind.Absolute, out var uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) ? uri : null; + + public static DiagnosticDescriptor WithMessageFormat(this DiagnosticDescriptor descriptor, LocalizableString messageFormat) + { +#pragma warning disable RS0030 // Do not used banned APIs - DiagnosticDescriptor .ctor is banned in this project, but fine to use here. + return new DiagnosticDescriptor(descriptor.Id, descriptor.Title, messageFormat, + descriptor.Category, descriptor.DefaultSeverity, descriptor.IsEnabledByDefault, + descriptor.Description, descriptor.HelpLinkUri, descriptor.CustomTags.ToArray()); +#pragma warning restore RS0030 // Do not used banned APIs + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs index 554ad47a50f1c..70a9a8130e605 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs @@ -358,7 +358,7 @@ void DispatchException(ControlFlowRegion fromRegion) continue; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } fromRegion = enclosing; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs index 188976904764e..e0a6fa653aa6b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs @@ -57,28 +57,28 @@ protected override BasicBlockAnalysisData AnalyzeLocalFunctionInvocationCore(IMe // Lambda target needs flow analysis, not supported/reachable in operation tree based analysis. protected override BasicBlockAnalysisData AnalyzeLambdaInvocationCore(IFlowAnonymousFunctionOperation lambda, CancellationToken cancellationToken) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override bool IsLValueFlowCapture(CaptureId captureId) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override bool IsRValueFlowCapture(CaptureId captureId) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void OnLValueCaptureFound(ISymbol symbol, IOperation operation, CaptureId captureId) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void OnLValueDereferenceFound(CaptureId captureId) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override bool IsTrackingDelegateCreationTargets => false; public override void SetLambdaTargetForDelegate(IOperation write, IFlowAnonymousFunctionOperation lambdaTarget) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void SetLocalFunctionTargetForDelegate(IOperation write, IMethodReferenceOperation localFunctionTarget) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void SetEmptyInvocationTargetsForDelegate(IOperation write) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void SetTargetsFromSymbolForDelegate(IOperation write, ISymbol symbol) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override bool TryGetDelegateInvocationTargets(IOperation write, out ImmutableHashSet targets) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void Dispose() { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs index 935c36cb727e8..1d59fb211c485 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs @@ -424,7 +424,7 @@ private void ProcessPossibleDelegateCreation(IOperation creation, IOperation wri // We don't support lambda target analysis for operation tree // and control flow graph should have replaced 'AnonymousFunction' nodes // with 'FlowAnonymousFunction' nodes. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); case OperationKind.FlowAnonymousFunction: _currentAnalysisData.SetLambdaTargetForDelegate(write, (IFlowAnonymousFunctionOperation)currentOperation); @@ -532,7 +532,7 @@ void AnalyzeDelegateInvocation(IOperation target) break; default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs index 0bcd0a798770d..b7c930e30ddab 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs @@ -69,7 +69,7 @@ public bool IsInitialParameterValueUsed(IParameterSymbol parameter) } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs index 435c79ec4c55f..4b69df2097e9b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs @@ -178,7 +178,7 @@ private int GetIndentationOfCurrentPosition( return Math.Max(0, indentationLevel + extraSpaces); } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } private (int indentation, IndentBlockOperation? operation) GetIndentationRuleOfCurrentPosition( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index ed521615ea61b..96256475649f9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -72,7 +72,7 @@ internal AbstractFormatEngine( // get span and common root this.SpanToFormat = GetSpanToFormat(); - _commonRoot = startToken.GetCommonRoot(endToken) ?? throw ExceptionUtilities.Unreachable; + _commonRoot = startToken.GetCommonRoot(endToken) ?? throw ExceptionUtilities.Unreachable(); } internal abstract IHeaderFacts HeaderFacts { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs index 209a1618ee55a..e2cfb69b20b67 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs @@ -71,7 +71,7 @@ public override TriviaData WithSpace(int space, FormattingContext context, Chain return this; } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override TriviaData WithLine( @@ -113,7 +113,7 @@ public override TriviaData WithLine( } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } public override TriviaData WithIndentation( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs index 0dbad6de7a670..c37693e892dd5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs @@ -177,7 +177,7 @@ protected bool IsNullOrWhitespace([NotNullWhen(true)] string? text) /// /// return line column rule for the given two trivia /// - protected abstract LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2); + protected abstract LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2, CancellationToken cancellationToken); /// /// format the given trivia at the line column position and put result to the changes list @@ -382,7 +382,7 @@ private LineColumn FormatFirstTriviaAndWhitespaceAfter( var lineColumnAfterTrivia1 = trivia1.RawKind == 0 ? lineColumnBeforeTrivia1 : lineColumnBeforeTrivia1.With(format(lineColumnBeforeTrivia1, trivia1, changes, cancellationToken)); - var rule = GetOverallLineColumnRuleBetween(trivia1, existingWhitespaceBetween, implicitLineBreak, trivia2); + var rule = GetOverallLineColumnRuleBetween(trivia1, existingWhitespaceBetween, implicitLineBreak, trivia2, cancellationToken); var whitespaceDelta = Apply(lineColumnBeforeTrivia1, trivia1, lineColumnAfterTrivia1, existingWhitespaceBetween, trivia2, rule); var span = GetTextSpan(trivia1, trivia2); @@ -394,9 +394,9 @@ private LineColumn FormatFirstTriviaAndWhitespaceAfter( /// /// get line column rule between two trivia /// - private LineColumnRule GetOverallLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2) + private LineColumnRule GetOverallLineColumnRuleBetween(SyntaxTrivia trivia1, LineColumnDelta existingWhitespaceBetween, bool implicitLineBreak, SyntaxTrivia trivia2, CancellationToken cancellationToken) { - var defaultRule = GetLineColumnRuleBetween(trivia1, existingWhitespaceBetween, implicitLineBreak, trivia2); + var defaultRule = GetLineColumnRuleBetween(trivia1, existingWhitespaceBetween, implicitLineBreak, trivia2, cancellationToken); GetTokensAtEdgeOfStructureTrivia(trivia1, trivia2, out var token1, out var token2); // if there are tokens, try formatting rules to see whether there is a user supplied one diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Serialization/SymbolSpecification.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Serialization/SymbolSpecification.cs index 9e86f38b3ca96..c25c355f299ac 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Serialization/SymbolSpecification.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Serialization/SymbolSpecification.cs @@ -408,7 +408,7 @@ internal XElement CreateXElement() SymbolCategory.Other => new XElement(nameof(SymbolKind), (SymbolKind)_kind), SymbolCategory.Type => new XElement(nameof(TypeKind), GetTypeKindString((TypeKind)_kind)), SymbolCategory.Method => new XElement(nameof(MethodKind), GetMethodKindString((MethodKind)_kind)), - _ => throw ExceptionUtilities.Unreachable + _ => throw ExceptionUtilities.Unreachable() }; private static string GetTypeKindString(TypeKind typeKind) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs index acf4349d05186..496fa7a9634bd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs @@ -89,8 +89,8 @@ internal partial interface ISemanticFacts IEnumerable GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken); - IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, CancellationToken cancellationToken); - IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, CancellationToken cancellationToken); + IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken); + IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken); ISymbol FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken); ISymbol FindFieldOrPropertyForAttributeArgument(SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs index 2580904f6ddd7..b336b5720d672 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs @@ -119,9 +119,9 @@ private static bool IsFloatingPoint([NotNullWhen(returnValue: true)] ITypeSymbol => type?.SpecialType is SpecialType.System_Single or SpecialType.System_Double; public static IParameterSymbol FindParameterForArgument(this ISemanticFacts semanticFacts, SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken) - => semanticFacts.FindParameterForArgument(semanticModel, argument, allowUncertainCandidates: false, cancellationToken); + => semanticFacts.FindParameterForArgument(semanticModel, argument, allowUncertainCandidates: false, allowParams: false, cancellationToken); public static IParameterSymbol FindParameterForAttributeArgument(this ISemanticFacts semanticFacts, SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken) - => semanticFacts.FindParameterForAttributeArgument(semanticModel, argument, allowUncertainCandidates: false, cancellationToken); + => semanticFacts.FindParameterForAttributeArgument(semanticModel, argument, allowUncertainCandidates: false, allowParams: false, cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 4dd0019c4f19e..3fc985a3b92fe 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -465,6 +465,7 @@ void GetPartsOfTupleExpression(SyntaxNode node, void GetPartsOfBinaryPattern(SyntaxNode node, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right); void GetPartsOfDeclarationPattern(SyntaxNode node, out SyntaxNode type, out SyntaxNode designation); void GetPartsOfRecursivePattern(SyntaxNode node, out SyntaxNode? type, out SyntaxNode? positionalPart, out SyntaxNode? propertyPart, out SyntaxNode? designation); + void GetPartsOfRelationalPattern(SyntaxNode node, out SyntaxToken operatorToken, out SyntaxNode expression); void GetPartsOfUnaryPattern(SyntaxNode node, out SyntaxToken operatorToken, out SyntaxNode pattern); bool ContainsInterleavedDirective(TextSpan span, SyntaxToken token, CancellationToken cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 6b9228b623e5e..32e8f01affb50 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -870,6 +870,9 @@ public static bool IsParenthesizedPattern(this ISyntaxFacts syntaxFacts, [NotNul public static bool IsRecursivePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern; + public static bool IsRelationalPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern; + public static bool IsTypePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.TypePattern; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index b6a15d53c6cb0..66d199ecc512f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -151,6 +151,7 @@ internal interface ISyntaxKinds int? OrPattern { get; } int? ParenthesizedPattern { get; } int? RecursivePattern { get; } + int? RelationalPattern { get; } int? TypePattern { get; } int? VarPattern { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyComparer.cs index 86afc6c9b2131..5301a8fc22595 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyComparer.cs @@ -18,22 +18,23 @@ private SymbolKeyComparer(ComparisonOptions options) public bool Equals(SymbolKey x, SymbolKey y) { - var comparer = _options.IgnoreCase - ? StringComparer.OrdinalIgnoreCase - : StringComparer.Ordinal; - if (!_options.IgnoreAssemblyKey) { // Easiest case. We can directly compare the raw contents of the keys. - return comparer.Equals(x._symbolKeyData, y._symbolKeyData); + return x.Equals(y, _options.IgnoreCase); } else { - // This is harder. To compare these we need to remove the entries related to - // assemblies. + // This is harder. To compare these we need to remove the entries related to assemblies. + // + // Note: this will remove the language-string as well, so we don't have to worry about that here. var data1 = RemoveAssemblyKeys(x._symbolKeyData); var data2 = RemoveAssemblyKeys(y._symbolKeyData); + var comparer = _options.IgnoreCase + ? StringComparer.OrdinalIgnoreCase + : StringComparer.Ordinal; + return comparer.Equals(data1, data2); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs index eb98622675d8f..4cef2dc7aa678 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs @@ -251,6 +251,11 @@ public void Initialize(string data) public string RemoveAssemblySymbolKeys() { + this.ReadFormatVersion(); + + // read out the language as well, it's not part of any symbol key comparison + this.SkipString(); + while (Position < Data.Length) { var ch = Data[Position]; @@ -260,15 +265,9 @@ public string RemoveAssemblySymbolKeys() var type = (SymbolKeyType)Data[Position]; _builder.Append(Eat(type)); - if (type == SymbolKeyType.Assembly) - { - Debug.Assert(_skipString == false); - _skipString = true; - ReadString(); - Debug.Assert(_skipString == true); - _skipString = false; - } + if (type == SymbolKeyType.Assembly) + SkipString(); } else if (Data[Position] == DoubleQuoteChar) { @@ -284,6 +283,17 @@ public string RemoveAssemblySymbolKeys() return _builder.ToString(); } + private void SkipString() + { + Debug.Assert(_skipString == false); + _skipString = true; + + ReadString(); + + Debug.Assert(_skipString == true); + _skipString = false; + } + protected override object? CreateResultForString(int start, int end, bool hasEmbeddedQuote) { // 'start' is right after the open quote, and 'end' is right before the close quote. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index 165b341d4593a..9765db25cb617 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -348,13 +348,13 @@ public override void VisitField(IFieldSymbol fieldSymbol) } public override void VisitLabel(ILabelSymbol labelSymbol) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void VisitLocal(ILocalSymbol localSymbol) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void VisitRangeVariable(IRangeVariableSymbol rangeVariableSymbol) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); public override void VisitMethod(IMethodSymbol methodSymbol) { @@ -383,7 +383,7 @@ public override void VisitMethod(IMethodSymbol methodSymbol) break; case MethodKind.LocalFunction: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); default: WriteType(SymbolKeyType.Method); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs index 4e5ffb35f541d..90c8f87c89b18 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs @@ -99,6 +99,12 @@ namespace Microsoft.CodeAnalysis /// versions may change the encoded format and may no longer be able to previous keys. As /// such, only persist if using for a cache that can be regenerated if necessary. /// + /// + /// The string values produced by (or ) should not be + /// directly compared for equality or used in hashing scenarios. Specifically, two symbol keys which represent the + /// 'same' symbol might produce different strings. Instead, to compare keys use + /// to get a suitable comparer that exposes the desired semantics. + /// /// [DataContract] internal partial struct SymbolKey : IEquatable @@ -120,7 +126,7 @@ internal partial struct SymbolKey : IEquatable /// from any other source is not supported. /// public SymbolKey(string data) - => _symbolKeyData = data ?? throw new ArgumentNullException(); + => _symbolKeyData = data ?? throw new ArgumentNullException(nameof(data)); /// /// Constructs a new representing the provided . @@ -188,6 +194,9 @@ public static SymbolKeyResolution ResolveString( return default; } + // Read out the language info which was included just for diagnostic purposes. + var language = reader.ReadString(); + // Initial entrypoint. No contextual symbol to pass along. var result = reader.ReadSymbolKey(contextualSymbol: null, out failureReason); Debug.Assert(reader.Position == symbolKey.Length); @@ -202,6 +211,12 @@ internal static string CreateStringWorker(int version, ISymbol? symbol, Cancella { using var writer = SymbolKeyWriter.GetWriter(cancellationToken); writer.WriteFormatVersion(version); + + // include the language just for help diagnosing issues. Note: the language is not considered part of the + // 'value' of the key. In other words two keys that represent the same symbol (like 'System.Int32'), but + // which differ on language, will still be considered equal. to each other. + writer.WriteString(symbol?.Language); + writer.WriteSymbolKey(symbol); return writer.CreateKey(); } @@ -265,9 +280,7 @@ private static bool ParameterRefKindsMatch( PooledArrayBuilder refKinds) { if (parameters.Length != refKinds.Count) - { return false; - } for (var i = 0; i < refKinds.Count; i++) { @@ -297,9 +310,7 @@ private static PooledArrayBuilder GetMembersOfNamedType( foreach (var member in members) { if (member is TSymbol symbol) - { result.AddIfNotNull(symbol); - } } } @@ -309,20 +320,55 @@ private static PooledArrayBuilder GetMembersOfNamedType( public static bool IsBodyLevelSymbol(ISymbol symbol) => symbol switch { - ILabelSymbol _ => true, - IRangeVariableSymbol _ => true, - ILocalSymbol _ => true, - IMethodSymbol { MethodKind: MethodKind.LocalFunction } _ => true, + ILabelSymbol => true, + IRangeVariableSymbol => true, + ILocalSymbol => true, + IMethodSymbol { MethodKind: MethodKind.LocalFunction } => true, _ => false, }; + private static int GetDataStartPosition(string key) + { + if (string.IsNullOrEmpty(key)) + return 0; + + using var reader = SymbolKeyReader.GetReader(key, compilation: null!, ignoreAssemblyKey: false, CancellationToken.None); + _ = reader.ReadFormatVersion(); + _ = reader.ReadString(); + return reader.Position; + } + public override int GetHashCode() - => _symbolKeyData.GetHashCode(); + { + var position = GetDataStartPosition(_symbolKeyData); + +#if NETSTANDARD + var hashCode = 0; + foreach (var ch in _symbolKeyData[position..]) + hashCode = Hash.Combine(ch, hashCode); + + return hashCode; +#else + return string.GetHashCode(_symbolKeyData.AsSpan(position)); +#endif + } public override bool Equals(object? obj) => obj is SymbolKey symbolKey && this.Equals(symbolKey); public bool Equals(SymbolKey other) - => _symbolKeyData == other._symbolKeyData; + => Equals(other, ignoreCase: false); + + private bool Equals(SymbolKey other, bool ignoreCase) + { + var position1 = GetDataStartPosition(_symbolKeyData); + var position2 = GetDataStartPosition(other._symbolKeyData); + + var keySpan1 = _symbolKeyData.AsSpan(position1); + var keySpan2 = other._symbolKeyData.AsSpan(position2); + + var comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + return keySpan1.Equals(keySpan2, comparison); + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs index 0f9fcca1e664b..beb42443acbcf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs @@ -429,11 +429,11 @@ private void StartAsynchronousComputation(AsynchronousComputationToStart computa // We can only be here if the computation was cancelled, which means all requests for the value // must have been cancelled. Therefore, the ThrowIfCancellationRequested above must have thrown // because that token from the requester was cancelled. - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.Builder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.Builder.cs index 016a49d579c8f..b7b5d27c5e07d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.Builder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.Builder.cs @@ -92,10 +92,10 @@ private class Builder private readonly Edge[] _compactEdges; private readonly BuilderNode[] _builderNodes; - public Builder(IEnumerable> values) + public Builder(IEnumerable values) { // TODO(cyrusn): Properly handle unicode normalization here. - var distinctValues = values.Where(v => v.Length > 0).Distinct(StringSliceComparer.OrdinalIgnoreCase).ToArray(); + var distinctValues = values.Where(v => v.Length > 0).Distinct(CaseInsensitiveComparison.Comparer).ToArray(); var charCount = values.Sum(v => v.Length); _concatenatedLowerCaseWords = new char[charCount]; @@ -107,7 +107,7 @@ public Builder(IEnumerable> values) var value = distinctValues[i]; _wordSpans[i] = new TextSpan(characterIndex, value.Length); - foreach (var ch in value.Span) + foreach (var ch in value) { _concatenatedLowerCaseWords[characterIndex] = CaseInsensitiveComparison.ToLower(ch); characterIndex++; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.cs index 4cabf69b29df9..6823dea1679f4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/BKTree.cs @@ -63,9 +63,9 @@ private BKTree(char[] concatenatedLowerCaseWords, ImmutableArray nodes, Im } public static BKTree Create(params string[] values) - => Create(values.Select(v => v.AsMemory())); + => Create((IEnumerable)values); - public static BKTree Create(IEnumerable> values) + public static BKTree Create(IEnumerable values) => new Builder(values).Create(); public IList Find(string value, int? threshold = null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs index 20f7b8efbc9e5..18264d5d5a8f3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs @@ -177,7 +177,7 @@ public void RaiseEvent(Action invoker, TArg arg) } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs index b21b3a4ef8929..5643cf32410cd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs @@ -51,10 +51,6 @@ public static Task> EmptyImmutableArray() public static Task> EmptyEnumerable() => EmptyTasks.EmptyEnumerable; - [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")] - public static Task FromResult(T t) where T : class - => FromResultCache.FromResult(t); - [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Naming is modeled after Task.WhenAll.")] public static ValueTask WhenAll(IEnumerable> tasks) { @@ -194,15 +190,5 @@ private static class EmptyTasks public static readonly Task> EmptyList = Task.FromResult(SpecializedCollections.EmptyList()); public static readonly Task> EmptyReadOnlyList = Task.FromResult(SpecializedCollections.EmptyReadOnlyList()); } - - private static class FromResultCache where T : class - { - private static readonly ConditionalWeakTable> s_fromResultCache = new(); - private static readonly ConditionalWeakTable>.CreateValueCallback s_taskCreationCallback = Task.FromResult; - - [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")] - public static Task FromResult(T t) - => s_fromResultCache.GetValue(t, s_taskCreationCallback); - } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/StringSlice.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/StringSlice.cs deleted file mode 100644 index 821e3d232e8a6..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/StringSlice.cs +++ /dev/null @@ -1,44 +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 Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Utilities -{ - internal abstract class StringSliceComparer : IComparer>, IEqualityComparer> - { - public static readonly StringSliceComparer Ordinal = new OrdinalComparer(); - public static readonly StringSliceComparer OrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(); - - private class OrdinalComparer : StringSliceComparer - { - public override int Compare(ReadOnlyMemory x, ReadOnlyMemory y) - => x.Span.CompareTo(y.Span, StringComparison.Ordinal); - - public override bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) - => x.Span.Equals(y.Span, StringComparison.Ordinal); - - public override int GetHashCode(ReadOnlyMemory obj) - => Hash.GetFNVHashCode(obj.Span); - } - - private class OrdinalIgnoreCaseComparer : StringSliceComparer - { - public override int Compare(ReadOnlyMemory x, ReadOnlyMemory y) - => CaseInsensitiveComparison.Compare(x.Span, y.Span); - - public override bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) - => CaseInsensitiveComparison.Equals(x.Span, y.Span); - - public override int GetHashCode(ReadOnlyMemory obj) - => Hash.GetCaseInsensitiveFNVHashCode(obj.Span); - } - - public abstract int Compare(ReadOnlyMemory x, ReadOnlyMemory y); - public abstract bool Equals(ReadOnlyMemory x, ReadOnlyMemory y); - public abstract int GetHashCode(ReadOnlyMemory obj); - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SyntaxPath.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SyntaxPath.cs index fa059ce30b0d5..cdfdd5d643547 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SyntaxPath.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SyntaxPath.cs @@ -80,7 +80,7 @@ private void AddSegment(SyntaxNodeOrToken nodeOrToken) } } - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskExtensions.cs index 5e1adc34fa59d..6973b3d6de850 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskExtensions.cs @@ -162,7 +162,7 @@ static TResult outerFunction(Task t, object? state) } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskFactoryExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskFactoryExtensions.cs index 7d7149a64266b..c56856f879bbc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskFactoryExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/TaskFactoryExtensions.cs @@ -26,7 +26,7 @@ void wrapped() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } @@ -44,7 +44,7 @@ TResult wrapped() } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } 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/Compiler/VisualBasic/Extensions/ExpressionSyntaxExtensions.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ExpressionSyntaxExtensions.vb index a0d382b89bdd6..645cbc00ee321 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ExpressionSyntaxExtensions.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ExpressionSyntaxExtensions.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -97,10 +98,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions expression.IsKind(SyntaxKind.TrueLiteralExpression) End Function - - Public Function DetermineType(expression As ExpressionSyntax, - semanticModel As SemanticModel, - cancellationToken As CancellationToken) As ITypeSymbol + + Public Function DetermineType( + expression As ExpressionSyntax, + semanticModel As SemanticModel, + cancellationToken As CancellationToken) As ITypeSymbol + ' If a parameter appears to have a void return type, then just use 'object' instead. If expression IsNot Nothing Then Dim typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken) @@ -109,6 +112,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Return semanticModel.Compilation.ObjectType End If + Dim returnType As ITypeSymbol = Nothing + If IsImplicitlyCallable(expression, semanticModel, cancellationToken, returnType) Then + Return returnType + End If + Dim symbol = If(typeInfo.Type, symbolInfo.GetAnySymbol()) If symbol IsNot Nothing Then Return symbol.ConvertToType(semanticModel.Compilation) @@ -118,11 +126,48 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Dim collectionInitializer = DirectCast(expression, CollectionInitializerSyntax) Return DetermineType(collectionInitializer, semanticModel, cancellationToken) End If + + Dim unary = TryCast(expression, UnaryExpressionSyntax) + If unary IsNot Nothing AndAlso unary.Kind() = SyntaxKind.AddressOfExpression Then + Return DetermineType(unary.Operand, semanticModel, cancellationToken) + End If End If Return semanticModel.Compilation.ObjectType End Function + + Public Function IsImplicitlyCallable( + expression As ExpressionSyntax, + semanticModel As SemanticModel, + cancellationToken As CancellationToken, + ByRef returnType As ITypeSymbol) As Boolean + + ' in VB if you have an expression that binds to a method, and that method has no required + ' parameters, then it's allowable to implicitly call that method just by referring to the name. + ' e.g. `WriteLine(SomeMethod)` is equivalent to `WriteLine(SomeMethod())`. So infer the return + ' type of 'SomeMethod' here, not a delegate type for it. + ' + ' Note: this does not apply if teh user wrote `WriteLine(AddressOf SomeMethod)` of course. + If expression IsNot Nothing Then + Dim symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken) + Dim methodSymbol = TryCast(symbolInfo.GetAnySymbol(), IMethodSymbol) + + Dim unaryParent = TryCast(expression.WalkUpParentheses().Parent, UnaryExpressionSyntax) + If unaryParent IsNot Nothing AndAlso + unaryParent.Kind() <> SyntaxKind.AddressOfExpression AndAlso + methodSymbol?.MethodKind = MethodKind.Ordinary AndAlso + Not methodSymbol.ReturnsByRef AndAlso + methodSymbol.Parameters.All(Function(p) p.IsOptional OrElse p.IsParams) Then + returnType = methodSymbol.ReturnType + Return True + End If + End If + + returnType = Nothing + Return False + End Function + Private Function DetermineType(collectionInitializer As CollectionInitializerSyntax, semanticModel As SemanticModel, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index bd0fe30f25af0..881527bfd575a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -227,11 +227,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return SpecializedCollections.SingletonEnumerable(semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken)) End Function - Public Function FindParameterForArgument(semanticModel As SemanticModel, argument As SyntaxNode, allowUncertainCandidates As Boolean, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFacts.FindParameterForArgument - Return DirectCast(argument, ArgumentSyntax).DetermineParameter(semanticModel, allowUncertainCandidates, allowParamArray:=False, cancellationToken) + Public Function FindParameterForArgument(semanticModel As SemanticModel, argument As SyntaxNode, allowUncertainCandidates As Boolean, allowParams As Boolean, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFacts.FindParameterForArgument + Return DirectCast(argument, ArgumentSyntax).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams, cancellationToken) End Function - Public Function FindParameterForAttributeArgument(semanticModel As SemanticModel, argument As SyntaxNode, allowUncertainCandidates As Boolean, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFacts.FindParameterForAttributeArgument + Public Function FindParameterForAttributeArgument(semanticModel As SemanticModel, argument As SyntaxNode, allowUncertainCandidates As Boolean, allowParams As Boolean, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFacts.FindParameterForAttributeArgument Return Nothing End Function diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index a23875e0724f7..6a6c3e2c613c6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1690,6 +1690,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Throw New InvalidOperationException(DoesNotExistInVBErrorMessage) End Sub + Public Sub GetPartsOfRelationalPattern(node As SyntaxNode, ByRef operatorToken As SyntaxToken, ByRef expression As SyntaxNode) Implements ISyntaxFacts.GetPartsOfRelationalPattern + Throw New InvalidOperationException(DoesNotExistInVBErrorMessage) + End Sub + Public Sub GetPartsOfDeclarationPattern(node As SyntaxNode, ByRef type As SyntaxNode, ByRef designation As SyntaxNode) Implements ISyntaxFacts.GetPartsOfDeclarationPattern Throw New InvalidOperationException(DoesNotExistInVBErrorMessage) End Sub diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 1ce6a58163ecf..9d3a3518c6f3c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -102,9 +102,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property OrPattern As Integer? Implements ISyntaxKinds.OrPattern Public ReadOnly Property ParenthesizedPattern As Integer? Implements ISyntaxKinds.ParenthesizedPattern Public ReadOnly Property RecursivePattern As Integer? Implements ISyntaxKinds.RecursivePattern + Public ReadOnly Property RelationalPattern As Integer? Implements ISyntaxKinds.RelationalPattern Public ReadOnly Property TypePattern As Integer? Implements ISyntaxKinds.TypePattern Public ReadOnly Property VarPattern As Integer? Implements ISyntaxKinds.VarPattern - Public ReadOnly Property IndexerMemberCref As Integer? Implements ISyntaxKinds.IndexerMemberCref Public ReadOnly Property EndOfFileToken As Integer = SyntaxKind.EndOfFileToken Implements ISyntaxKinds.EndOfFileToken Public ReadOnly Property AwaitKeyword As Integer = SyntaxKind.AwaitKeyword Implements ISyntaxKinds.AwaitKeyword @@ -146,5 +146,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property Interpolation As Integer = SyntaxKind.Interpolation Implements ISyntaxKinds.Interpolation Public ReadOnly Property InterpolatedStringExpression As Integer = SyntaxKind.InterpolatedStringExpression Implements ISyntaxKinds.InterpolatedStringExpression Public ReadOnly Property InterpolatedStringText As Integer = SyntaxKind.InterpolatedStringText Implements ISyntaxKinds.InterpolatedStringText + Public ReadOnly Property IndexerMemberCref As Integer? Implements ISyntaxKinds.IndexerMemberCref End Class End Namespace diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems index 9ad66776faa0c..7090798e7bdfd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CSharpWorkspaceExtensions.projitems @@ -11,6 +11,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseArgumentListSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseArgumentListSyntaxExtensions.cs index 9fa948d1b71b6..73b0c8438777c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseArgumentListSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseArgumentListSyntaxExtensions.cs @@ -9,35 +9,19 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions internal static class BaseArgumentListSyntaxExtensions { public static SyntaxToken GetOpenToken(this BaseArgumentListSyntax node) - { - if (node != null) + => node switch { - switch (node.Kind()) - { - case SyntaxKind.ArgumentList: - return ((ArgumentListSyntax)node).OpenParenToken; - case SyntaxKind.BracketedArgumentList: - return ((BracketedArgumentListSyntax)node).OpenBracketToken; - } - } - - return default; - } + ArgumentListSyntax list => list.OpenParenToken, + BracketedArgumentListSyntax bracketedList => bracketedList.OpenBracketToken, + _ => default, + }; public static SyntaxToken GetCloseToken(this BaseArgumentListSyntax node) - { - if (node != null) + => node switch { - switch (node.Kind()) - { - case SyntaxKind.ArgumentList: - return ((ArgumentListSyntax)node).CloseParenToken; - case SyntaxKind.BracketedArgumentList: - return ((BracketedArgumentListSyntax)node).CloseBracketToken; - } - } - - return default; - } + ArgumentListSyntax list => list.CloseParenToken, + BracketedArgumentListSyntax bracketedList => bracketedList.CloseBracketToken, + _ => default, + }; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseParameterListSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseParameterListSyntaxExtensions.cs new file mode 100644 index 0000000000000..a96f4383a322f --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/BaseParameterListSyntaxExtensions.cs @@ -0,0 +1,27 @@ +// 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.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Extensions +{ + internal static class BaseParameterListSyntaxExtensions + { + public static SyntaxToken GetOpenToken(this BaseParameterListSyntax node) + => node switch + { + ParameterListSyntax list => list.OpenParenToken, + BracketedParameterListSyntax bracketedList => bracketedList.OpenBracketToken, + _ => default, + }; + + public static SyntaxToken GetCloseToken(this BaseParameterListSyntax node) + => node switch + { + ParameterListSyntax list => list.CloseParenToken, + BracketedParameterListSyntax bracketedList => bracketedList.CloseBracketToken, + _ => default, + }; + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs index 0899a54402b44..22890db620c2c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs @@ -6,7 +6,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; @@ -341,6 +340,19 @@ public bool IsTypeDeclarationContext( return this.SyntaxTree.IsTypeDeclarationContext(this.Position, this, validModifiers, validTypeDeclarations, canBePartial, cancellationToken); } + public bool IsRecordDeclarationContext(ISet validModifiers, CancellationToken cancellationToken) + { + var previousToken = LeftToken.GetPreviousTokenIfTouchingWord(Position); + + if (!previousToken.IsKind(SyntaxKind.RecordKeyword)) + return false; + + var positionBeforeRecordKeyword = previousToken.SpanStart; + var modifiers = SyntaxTree.GetPrecedingModifiers(positionBeforeRecordKeyword, cancellationToken); + + return modifiers.IsProperSubsetOf(validModifiers); + } + public bool IsMemberAttributeContext(ISet validTypeDeclarations, CancellationToken cancellationToken) { // cases: diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 0614c628d2e41..a83864dae6b92 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -1963,14 +1963,19 @@ public static bool IsInstanceContext(this SyntaxTree syntaxTree, SyntaxToken tar } #endif - var enclosingSymbol = semanticModel.GetEnclosingSymbol(targetToken.SpanStart, cancellationToken); + // It's possible the caller is asking about a speculative semantic model, and may have moved before the + // bounds of that model (for example, while looking at the nearby tokens around an edit). If so, ensure we + // walk outwards to the correct model to actually ask this question of. + var position = targetToken.SpanStart; + if (semanticModel.IsSpeculativeSemanticModel && position < semanticModel.OriginalPositionForSpeculation) + semanticModel = semanticModel.GetOriginalSemanticModel(); - while (enclosingSymbol is IMethodSymbol method && (method.MethodKind == MethodKind.LocalFunction || method.MethodKind == MethodKind.AnonymousFunction)) + var enclosingSymbol = semanticModel.GetEnclosingSymbol(position, cancellationToken); + + while (enclosingSymbol is IMethodSymbol { MethodKind: MethodKind.LocalFunction or MethodKind.AnonymousFunction } method) { if (method.IsStatic) - { return false; - } // It is allowed to reference the instance (`this`) within a local function or anonymous function, as long as the containing method allows it enclosingSymbol = enclosingSymbol.ContainingSymbol; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs index 11062d40825fd..b462979bea1fc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs @@ -142,7 +142,7 @@ public static bool ContainingTypesOrSelfHasUnsafeKeyword(this ITypeSymbol contai } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.General)) { - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs index 2b6c36017e27b..0e288befc1133 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs @@ -167,7 +167,7 @@ private SyntaxNode VisitBaseNamespaceDeclaration( public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node) { // recurse downwards so we visit inner namespaces first. - var rewritten = (CompilationUnitSyntax)(base.VisitCompilationUnit(node) ?? throw ExceptionUtilities.Unreachable); + var rewritten = (CompilationUnitSyntax)(base.VisitCompilationUnit(node) ?? throw ExceptionUtilities.Unreachable()); if (!node.CanAddUsingDirectives(_options.AllowInHiddenRegions, _cancellationToken)) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs index 08f7466dbab65..8f87f4b468b20 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs @@ -143,7 +143,7 @@ public override SyntaxNode NegateEquality(SyntaxGenerator generator, SyntaxNode }; public override SyntaxNode IsNotTypeExpression(SyntaxNode expression, SyntaxNode type) - => throw ExceptionUtilities.Unreachable; + => throw ExceptionUtilities.Unreachable(); #region Patterns @@ -156,6 +156,9 @@ public override SyntaxNode IsPatternExpression(SyntaxNode expression, SyntaxToke isKeyword == default ? SyntaxFactory.Token(SyntaxKind.IsKeyword) : isKeyword, (PatternSyntax)pattern); + public override SyntaxNode AndPattern(SyntaxNode left, SyntaxNode right) + => SyntaxFactory.BinaryPattern(SyntaxKind.AndPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right)); + public override SyntaxNode ConstantPattern(SyntaxNode expression) => SyntaxFactory.ConstantPattern((ExpressionSyntax)expression); @@ -164,8 +167,17 @@ public override SyntaxNode DeclarationPattern(INamedTypeSymbol type, string name type.GenerateTypeSyntax(), SyntaxFactory.SingleVariableDesignation(name.ToIdentifierToken())); - public override SyntaxNode AndPattern(SyntaxNode left, SyntaxNode right) - => SyntaxFactory.BinaryPattern(SyntaxKind.AndPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right)); + public override SyntaxNode LessThanRelationalPattern(SyntaxNode expression) + => SyntaxFactory.RelationalPattern(SyntaxFactory.Token(SyntaxKind.LessThanToken), (ExpressionSyntax)expression); + + public override SyntaxNode LessThanEqualsRelationalPattern(SyntaxNode expression) + => SyntaxFactory.RelationalPattern(SyntaxFactory.Token(SyntaxKind.LessThanEqualsToken), (ExpressionSyntax)expression); + + public override SyntaxNode GreaterThanRelationalPattern(SyntaxNode expression) + => SyntaxFactory.RelationalPattern(SyntaxFactory.Token(SyntaxKind.GreaterThanToken), (ExpressionSyntax)expression); + + public override SyntaxNode GreaterThanEqualsRelationalPattern(SyntaxNode expression) + => SyntaxFactory.RelationalPattern(SyntaxFactory.Token(SyntaxKind.GreaterThanEqualsToken), (ExpressionSyntax)expression); public override SyntaxNode NotPattern(SyntaxNode pattern) => SyntaxFactory.UnaryPattern(SyntaxFactory.Token(SyntaxKind.NotKeyword), (PatternSyntax)Parenthesize(pattern)); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs index df519676b4996..7e23c66c9eefe 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs @@ -51,32 +51,23 @@ internal sealed record class CodeActionOptions public const int DefaultConditionalExpressionWrappingLength = 120; #if !CODE_STYLE - [DataMember(Order = 0)] public CodeCleanupOptions CleanupOptions { get; init; } - [DataMember(Order = 1)] public CodeGenerationOptions CodeGenerationOptions { get; init; } - [DataMember(Order = 2)] public IdeCodeStyleOptions CodeStyleOptions { get; init; } - [DataMember(Order = 3)] public SymbolSearchOptions SearchOptions { get; init; } = SymbolSearchOptions.Default; - [DataMember(Order = 4)] public ImplementTypeOptions ImplementTypeOptions { get; init; } = ImplementTypeOptions.Default; - [DataMember(Order = 5)] public ExtractMethodOptions ExtractMethodOptions { get; init; } = ExtractMethodOptions.Default; - [DataMember(Order = 6)] public bool HideAdvancedMembers { get; init; } = false; - [DataMember(Order = 7)] public int WrappingColumn { get; init; } = DefaultWrappingColumn; - [DataMember(Order = 8)] public int ConditionalExpressionWrappingLength { get; init; } = DefaultConditionalExpressionWrappingLength; - [DataMember(Order = 9)] public bool EnableConvertToRecord { get; init; } = false; - - public CodeActionOptions( - CodeCleanupOptions cleanupOptions, - CodeGenerationOptions codeGenerationOptions, - IdeCodeStyleOptions codeStyleOptions) - { - CleanupOptions = cleanupOptions; - CodeGenerationOptions = codeGenerationOptions; - CodeStyleOptions = codeStyleOptions; - } + [DataMember] public required CodeCleanupOptions CleanupOptions { get; init; } + [DataMember] public required CodeGenerationOptions CodeGenerationOptions { get; init; } + [DataMember] public required IdeCodeStyleOptions CodeStyleOptions { get; init; } + [DataMember] public SymbolSearchOptions SearchOptions { get; init; } = SymbolSearchOptions.Default; + [DataMember] public ImplementTypeOptions ImplementTypeOptions { get; init; } = ImplementTypeOptions.Default; + [DataMember] public ExtractMethodOptions ExtractMethodOptions { get; init; } = ExtractMethodOptions.Default; + [DataMember] public bool HideAdvancedMembers { get; init; } = false; + [DataMember] public int WrappingColumn { get; init; } = DefaultWrappingColumn; + [DataMember] public int ConditionalExpressionWrappingLength { get; init; } = DefaultConditionalExpressionWrappingLength; public static CodeActionOptions GetDefault(LanguageServices languageServices) - => new( - CodeCleanupOptions.GetDefault(languageServices), - CodeGenerationOptions.GetDefault(languageServices), - IdeCodeStyleOptions.GetDefault(languageServices)); + => new() + { + CleanupOptions = CodeCleanupOptions.GetDefault(languageServices), + CodeGenerationOptions = CodeGenerationOptions.GetDefault(languageServices), + CodeStyleOptions = IdeCodeStyleOptions.GetDefault(languageServices) + }; #else public static CodeActionOptions GetDefault(LanguageServices languageServices) => new(); @@ -135,13 +126,21 @@ ValueTask OptionsProvider.GetOpt ValueTask OptionsProvider.GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) { var codeActionOptions = GetOptions(languageServices); - return ValueTaskFactory.FromResult(new CleanCodeGenerationOptions(codeActionOptions.CodeGenerationOptions, codeActionOptions.CleanupOptions)); + return ValueTaskFactory.FromResult(new CleanCodeGenerationOptions() + { + GenerationOptions = codeActionOptions.CodeGenerationOptions, + CleanupOptions = codeActionOptions.CleanupOptions + }); } ValueTask OptionsProvider.GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) { var codeActionOptions = GetOptions(languageServices); - return ValueTaskFactory.FromResult(new CodeAndImportGenerationOptions(codeActionOptions.CodeGenerationOptions, codeActionOptions.CleanupOptions.AddImportOptions)); + return ValueTaskFactory.FromResult(new CodeAndImportGenerationOptions() + { + GenerationOptions = codeActionOptions.CodeGenerationOptions, + AddImportOptions = codeActionOptions.CleanupOptions.AddImportOptions + }); } #endif } @@ -181,8 +180,9 @@ public static ImplementTypeGenerationOptions GetImplementTypeGenerationOptions(t public static ExtractMethodGenerationOptions GetExtractMethodGenerationOptions(this CodeActionOptionsProvider provider, LanguageServices languageServices) { var codeActionOptions = provider.GetOptions(languageServices); - return new(codeActionOptions.CodeGenerationOptions) + return new() { + CodeGenerationOptions = codeActionOptions.CodeGenerationOptions, ExtractOptions = codeActionOptions.ExtractMethodOptions, AddImportOptions = codeActionOptions.CleanupOptions.AddImportOptions, LineFormattingOptions = codeActionOptions.CleanupOptions.FormattingOptions.LineFormatting 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/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs index 45621cf7b81f3..50e58743e93c9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs @@ -48,6 +48,18 @@ public static SyntaxNode Negate( SemanticModel semanticModel, bool negateBinary, CancellationToken cancellationToken) + { + return Negate(generator, generatorInternal, expressionOrPattern, semanticModel, negateBinary, patternValueType: null, cancellationToken); + } + + public static SyntaxNode Negate( + this SyntaxGenerator generator, + SyntaxGeneratorInternal generatorInternal, + SyntaxNode expressionOrPattern, + SemanticModel semanticModel, + bool negateBinary, + SpecialType? patternValueType, + CancellationToken cancellationToken) { var options = semanticModel.SyntaxTree.Options; var syntaxFacts = generatorInternal.SyntaxFacts; @@ -93,7 +105,7 @@ public static SyntaxNode Negate( return GetNegationOfBinaryPattern(expressionOrPattern, generator, generatorInternal, semanticModel, cancellationToken); if (syntaxFacts.IsConstantPattern(expressionOrPattern)) - return GetNegationOfConstantPattern(expressionOrPattern, generator, generatorInternal); + return GetNegationOfConstantPattern(expressionOrPattern, generator, generatorInternal, patternValueType); if (syntaxFacts.IsUnaryPattern(expressionOrPattern)) return GetNegationOfUnaryPattern(expressionOrPattern, generator, generatorInternal, syntaxFacts); @@ -114,9 +126,10 @@ public static SyntaxNode Negate( return generator.IsTypeExpression(expression, type); } - // TODO(cyrusn): We could support negating relational patterns in the future. i.e. - // - // not >= 0 -> < 0 + if (syntaxFacts.IsRelationalPattern(expressionOrPattern)) + { + return GetNegationOfRelationalPattern(expressionOrPattern, generatorInternal, patternValueType); + } return syntaxFacts.IsAnyPattern(expressionOrPattern) ? generatorInternal.NotPattern(expressionOrPattern) @@ -157,6 +170,17 @@ private static SyntaxNode GetNegationOfBinaryExpression( if (!s_negatedBinaryMap.TryGetValue(binaryOperation.OperatorKind, out var negatedKind)) return generator.LogicalNotExpression(expressionNode); + // Lifted relational operators return false if either operand is null. + // Inverting the operator fails to invert the behavior when an operand is null. + if (binaryOperation.IsLifted + && binaryOperation.OperatorKind is BinaryOperatorKind.LessThan or + BinaryOperatorKind.LessThanOrEqual or + BinaryOperatorKind.GreaterThan or + BinaryOperatorKind.GreaterThanOrEqual) + { + return generator.LogicalNotExpression(expressionNode); + } + if (binaryOperation.OperatorKind is BinaryOperatorKind.Or or BinaryOperatorKind.And or BinaryOperatorKind.ConditionalAnd or @@ -184,7 +208,7 @@ private static SyntaxNode GetNegationOfBinaryPattern( SemanticModel semanticModel, CancellationToken cancellationToken) { - // Apply demorgans's law here. + // Apply De Morgan's laws here. // // not (a and b) -> not a or not b // not (a or b) -> not a and not b @@ -218,7 +242,11 @@ private static SyntaxNode GetNegationOfIsPatternExpression(SyntaxNode isExpressi if (syntaxFacts.SupportsNotPattern(semanticModel.SyntaxTree.Options)) { // We do support 'not' patterns. So attempt to push a 'not' pattern into the current is-pattern RHS. - negatedPattern = generator.Negate(generatorInternal, pattern, semanticModel, cancellationToken); + // We include the type of the value when negating the pattern, since it allows for nicer negations of + // `is true/false` for Boolean values and relational patterns for numeric values. + var operation = semanticModel.GetOperation(isExpression, cancellationToken); + var valueType = (operation as IIsPatternOperation)?.Value.Type?.SpecialType; + negatedPattern = generator.Negate(generatorInternal, pattern, semanticModel, negateBinary: true, valueType, cancellationToken); } else if (syntaxFacts.IsNotPattern(pattern)) { @@ -245,6 +273,33 @@ private static SyntaxNode GetNegationOfIsPatternExpression(SyntaxNode isExpressi return generator.LogicalNotExpression(isExpression); } + private static SyntaxNode GetNegationOfRelationalPattern( + SyntaxNode expressionNode, + SyntaxGeneratorInternal generatorInternal, + SpecialType? patternValueType) + { + if (patternValueType is SpecialType specialType && specialType.IsNumericType()) + { + // If we know the value is numeric, we can negate the relational operator. + // This is not valid for non-numeric value since they never match a relational pattern. + // Similarly, it's not valid for nullable values, since null never matches a relational pattern. + // As an example, `!(new object() is < 1)` is equivalent to `new object() is not < 1` but not `new object() is >= 1`. + var syntaxFacts = generatorInternal.SyntaxFacts; + syntaxFacts.GetPartsOfRelationalPattern(expressionNode, out var operatorToken, out var expression); + syntaxFacts.TryGetPredefinedOperator(operatorToken, out var predefinedOperator); + return predefinedOperator switch + { + PredefinedOperator.LessThan => generatorInternal.GreaterThanEqualsRelationalPattern(expression), + PredefinedOperator.LessThanOrEqual => generatorInternal.GreaterThanRelationalPattern(expression), + PredefinedOperator.GreaterThan => generatorInternal.LessThanEqualsRelationalPattern(expression), + PredefinedOperator.GreaterThanOrEqual => generatorInternal.LessThanRelationalPattern(expression), + _ => generatorInternal.NotPattern(expressionNode) + }; + } + + return generatorInternal.NotPattern(expressionNode); + } + private static bool IsLegalPattern(ISyntaxFacts syntaxFacts, SyntaxNode pattern, bool designatorsLegal) { // It is illegal to create a pattern that has a designator under a not-pattern or or-pattern @@ -412,18 +467,23 @@ private static SyntaxNode GetNegationOfLiteralExpression( private static SyntaxNode GetNegationOfConstantPattern( SyntaxNode pattern, SyntaxGenerator generator, - SyntaxGeneratorInternal generatorInternal) + SyntaxGeneratorInternal generatorInternal, + SpecialType? patternValueType) { var syntaxFacts = generatorInternal.SyntaxFacts; - // If we have `is true/false` just swap that to be `is false/true`. - - var expression = syntaxFacts.GetExpressionOfConstantPattern(pattern); - if (syntaxFacts.IsTrueLiteralExpression(expression)) - return generatorInternal.ConstantPattern(generator.FalseLiteralExpression()); + // If we have `is true/false` and a Boolean value, just swap that to be `is false/true`. + // If the value isn't a Boolean, swapping to `is false/true` is incorrect since non-Booleans match neither. + // As an example, `!(new object() is true)` is equivalent to `new object() is not true` but not `new object() is false`. + if (patternValueType == SpecialType.System_Boolean) + { + var expression = syntaxFacts.GetExpressionOfConstantPattern(pattern); + if (syntaxFacts.IsTrueLiteralExpression(expression)) + return generatorInternal.ConstantPattern(generator.FalseLiteralExpression()); - if (syntaxFacts.IsFalseLiteralExpression(expression)) - return generatorInternal.ConstantPattern(generator.TrueLiteralExpression()); + if (syntaxFacts.IsFalseLiteralExpression(expression)) + return generatorInternal.ConstantPattern(generator.TrueLiteralExpression()); + } // Otherwise, just negate the entire pattern, we don't have anything else special we can do here. return generatorInternal.NotPattern(pattern); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/TextDocumentExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/TextDocumentExtensions.cs index 8b1b259a254e5..31e64f72866cd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/TextDocumentExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/TextDocumentExtensions.cs @@ -26,7 +26,7 @@ public static TextDocument WithText(this TextDocument textDocument, SourceText t return additionalDocument.WithAdditionalDocumentText(text); default: - throw ExceptionUtilities.Unreachable; + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index 48cd2011275b8..74fe45e631c31 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -153,11 +153,11 @@ public bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToke public IEnumerable GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) => SemanticFacts.GetDeclaredSymbols(semanticModel, memberDeclaration, cancellationToken); - public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, CancellationToken cancellationToken) - => SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates, cancellationToken); + public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + => SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken); - public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, CancellationToken cancellationToken) - => SemanticFacts.FindParameterForAttributeArgument(semanticModel, argumentNode, allowUncertainCandidates, cancellationToken); + public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + => SemanticFacts.FindParameterForAttributeArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken); public ISymbol FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) => SemanticFacts.FindFieldOrPropertyForArgument(semanticModel, argumentNode, cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs index 155b3d46b86a9..f523bd222b5e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs @@ -100,8 +100,12 @@ public SyntaxNode LocalDeclarationStatement(SyntaxToken name, SyntaxNode initial public abstract SyntaxNode IsPatternExpression(SyntaxNode expression, SyntaxToken isToken, SyntaxNode pattern); public abstract SyntaxNode AndPattern(SyntaxNode left, SyntaxNode right); - public abstract SyntaxNode DeclarationPattern(INamedTypeSymbol type, string name); public abstract SyntaxNode ConstantPattern(SyntaxNode expression); + public abstract SyntaxNode DeclarationPattern(INamedTypeSymbol type, string name); + public abstract SyntaxNode GreaterThanRelationalPattern(SyntaxNode expression); + public abstract SyntaxNode GreaterThanEqualsRelationalPattern(SyntaxNode expression); + public abstract SyntaxNode LessThanRelationalPattern(SyntaxNode expression); + public abstract SyntaxNode LessThanEqualsRelationalPattern(SyntaxNode expression); public abstract SyntaxNode NotPattern(SyntaxNode pattern); public abstract SyntaxNode OrPattern(SyntaxNode left, SyntaxNode right); public abstract SyntaxNode ParenthesizedPattern(SyntaxNode pattern); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs index 61a3336f9b59e..fa6f32d8f075d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs @@ -3,11 +3,14 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -55,4 +58,34 @@ public ParsedDocument WithChangedRoot(SyntaxNode root, CancellationToken cancell var text = root.SyntaxTree.GetText(cancellationToken); return new ParsedDocument(Id, text, root, HostLanguageServices); } + + public ParsedDocument WithChange(TextChange change, CancellationToken cancellationToken) + => WithChangedText(Text.WithChanges(change), cancellationToken); + + public ParsedDocument WithChanges(IEnumerable changes, CancellationToken cancellationToken) + => WithChangedText(Text.WithChanges(changes), cancellationToken); + + /// + /// Equivalent semantics to + /// + public IEnumerable GetChanges(in ParsedDocument oldDocument) + { + Contract.ThrowIfFalse(Id == oldDocument.Id); + + if (Text == oldDocument.Text || SyntaxTree == oldDocument.SyntaxTree) + { + return SpecializedCollections.EmptyEnumerable(); + } + + var textChanges = Text.GetTextChanges(oldDocument.Text); + + // if changes are significant (not the whole document being replaced) then use these changes + if (textChanges.Count > 1 || + textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldDocument.Text.Length)) + { + return textChanges; + } + + return SyntaxTree.GetChanges(oldDocument.SyntaxTree); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb index 642ebcca78f09..de1e600a78f99 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb @@ -241,13 +241,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery End Function Private Function ComputeEnclosingNamedType(cancellationToken As CancellationToken) As INamedTypeSymbol - Dim enclosingSymbol = Me.SemanticModel.GetEnclosingSymbol(Me.TargetToken.SpanStart, cancellationToken) - Dim container = TryCast(enclosingSymbol, INamedTypeSymbol) - If container Is Nothing Then - container = enclosingSymbol.ContainingType + ' It's possible the caller is asking about a speculative semantic model, and may have moved before the + ' bounds of that model (for example, while looking at the nearby tokens around an edit). If so, ensure we + ' walk outwards to the correct model to actually ask this question of. + Dim position = TargetToken.SpanStart + Dim model = Me.SemanticModel + If model.IsSpeculativeSemanticModel AndAlso position < model.OriginalPositionForSpeculation Then + model = model.GetOriginalSemanticModel() End If - Return container + Dim enclosingSymbol = model.GetEnclosingSymbol(position, cancellationToken) + Return If(TryCast(enclosingSymbol, INamedTypeSymbol), enclosingSymbol.ContainingType) End Function Private Shared Function ComputeIsWithinPreprocessorContext(position As Integer, targetToken As SyntaxToken) As Boolean diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb index 280630681a2e8..de4f014b21889 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb @@ -156,6 +156,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Throw New NotImplementedException() End Function + Public Overrides Function AndPattern(left As SyntaxNode, right As SyntaxNode) As SyntaxNode + Throw New NotImplementedException() + End Function + Public Overrides Function ConstantPattern(expression As SyntaxNode) As SyntaxNode Throw New NotImplementedException() End Function @@ -164,7 +168,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Throw New NotImplementedException() End Function - Public Overrides Function AndPattern(left As SyntaxNode, right As SyntaxNode) As SyntaxNode + Public Overrides Function LessThanRelationalPattern(expression As SyntaxNode) As SyntaxNode + Throw New NotImplementedException() + End Function + + Public Overrides Function LessThanEqualsRelationalPattern(expression As SyntaxNode) As SyntaxNode + Throw New NotImplementedException() + End Function + + Public Overrides Function GreaterThanRelationalPattern(expression As SyntaxNode) As SyntaxNode + Throw New NotImplementedException() + End Function + + Public Overrides Function GreaterThanEqualsRelationalPattern(expression As SyntaxNode) As SyntaxNode Throw New NotImplementedException() End Function diff --git a/src/Workspaces/TestSourceGenerator/Microsoft.CodeAnalysis.TestSourceGenerator.csproj b/src/Workspaces/TestSourceGenerator/Microsoft.CodeAnalysis.TestSourceGenerator.csproj index 4422150f411dd..bcc06f8c16b52 100644 --- a/src/Workspaces/TestSourceGenerator/Microsoft.CodeAnalysis.TestSourceGenerator.csproj +++ b/src/Workspaces/TestSourceGenerator/Microsoft.CodeAnalysis.TestSourceGenerator.csproj @@ -3,6 +3,7 @@ netstandard2.0 + false diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/AttributeGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/AttributeGenerator.vb index 35ce4c3eb4be0..28bfc9ac788b7 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/AttributeGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/AttributeGenerator.vb @@ -12,11 +12,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Module AttributeGenerator Public Function GenerateAttributeBlocks(attributes As ImmutableArray(Of AttributeData), options As CodeGenerationContextInfo, Optional target As SyntaxToken? = Nothing) As SyntaxList(Of AttributeListSyntax) + attributes = attributes.WhereAsArray(Function(a) Not IsCompilerInternalAttribute(a)) + If Not attributes.Any() Then Return Nothing End If - Return SyntaxFactory.List(Of AttributeListSyntax)(attributes.OrderBy(Function(a) a.AttributeClass.Name).Select(Function(a) GenerateAttributeBlock(a, options, target))) + Return SyntaxFactory.List(attributes.OrderBy(Function(a) a.AttributeClass.Name).Select(Function(a) GenerateAttributeBlock(a, options, target))) End Function Private Function GenerateAttributeBlock(attribute As AttributeData, options As CodeGenerationContextInfo, target As SyntaxToken?) As AttributeListSyntax diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb index b4b13bce7fa7d..0f91ee320806e 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb @@ -11,7 +11,6 @@ Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.LanguageService -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/SemanticModelExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/SemanticModelExtensions.vb index 602db7452a025..31448a0abfef6 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/SemanticModelExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/SemanticModelExtensions.vb @@ -116,19 +116,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions ' parameter. If we have an expression that has a name as part of it ' then we try to use that part. Dim current = expression - - While True - current = current.WalkDownParentheses() - If current.Kind = SyntaxKind.IdentifierName Then - Return (DirectCast(current, IdentifierNameSyntax)).Identifier.ValueText.ToCamelCase() - ElseIf TypeOf current Is MemberAccessExpressionSyntax Then - Return (DirectCast(current, MemberAccessExpressionSyntax)).Name.Identifier.ValueText.ToCamelCase() - ElseIf TypeOf current Is CastExpressionSyntax Then - current = (DirectCast(current, CastExpressionSyntax)).Expression - Else - Exit While - End If - End While + Dim returnType As ITypeSymbol = Nothing + + ' If we have an implicitly callable expression (like `WriteLine(SomeMethod)`) we don't want to generate + ' `someMethod` as the name of the parameter. Just fallback to our default naming strategy. + If Not IsImplicitlyCallable(expression, semanticModel, cancellationToken, returnType) Then + While True + current = current.WalkDownParentheses() + If current.Kind = SyntaxKind.IdentifierName Then + Return (DirectCast(current, IdentifierNameSyntax)).Identifier.ValueText.ToCamelCase() + ElseIf TypeOf current Is MemberAccessExpressionSyntax Then + Return (DirectCast(current, MemberAccessExpressionSyntax)).Name.Identifier.ValueText.ToCamelCase() + ElseIf TypeOf current Is CastExpressionSyntax Then + current = (DirectCast(current, CastExpressionSyntax)).Expression + Else + Exit While + End If + End While + End If ' there was nothing in the expression to signify a name. If we're in an argument ' location, then try to choose a name based on the argument name. diff --git a/src/Workspaces/VisualBasic/Portable/FindSymbols/VisualBasicDeclaredSymbolInfoFactoryService.vb b/src/Workspaces/VisualBasic/Portable/FindSymbols/VisualBasicDeclaredSymbolInfoFactoryService.vb index 67a1a71ffb55e..053dcb4224475 100644 --- a/src/Workspaces/VisualBasic/Portable/FindSymbols/VisualBasicDeclaredSymbolInfoFactoryService.vb +++ b/src/Workspaces/VisualBasic/Portable/FindSymbols/VisualBasicDeclaredSymbolInfoFactoryService.vb @@ -142,31 +142,35 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName As String, fullyQualifiedContainerName As String) As DeclaredSymbolInfo? + Dim blockStatement = typeDeclaration.BlockStatement + ' If this Is a part of partial type that only contains nested types, then we don't make an info type for it. ' That's because we effectively think of this as just being a virtual container just to hold the nested ' types, And Not something someone would want to explicitly navigate to itself. Similar to how we think of ' namespaces. - If typeDeclaration.BlockStatement.Modifiers.Any(SyntaxKind.PartialKeyword) AndAlso + If blockStatement.Modifiers.Any(SyntaxKind.PartialKeyword) AndAlso typeDeclaration.Members.Any() AndAlso - typeDeclaration.Members.All(Function(m) TypeOf m Is TypeBlockSyntax) Then +typeDeclaration.Members.All(Function(m) TypeOf m Is TypeBlockSyntax) Then Return Nothing End If Return DeclaredSymbolInfo.Create( stringTable, - typeDeclaration.BlockStatement.Identifier.ValueText, - GetTypeParameterSuffix(typeDeclaration.BlockStatement.TypeParameterList), + blockStatement.Identifier.ValueText, + GetTypeParameterSuffix(blockStatement.TypeParameterList), containerDisplayName, fullyQualifiedContainerName, - typeDeclaration.BlockStatement.Modifiers.Any(SyntaxKind.PartialKeyword), + blockStatement.Modifiers.Any(SyntaxKind.PartialKeyword), + blockStatement.AttributeLists.Any(), If(typeDeclaration.Kind() = SyntaxKind.ClassBlock, DeclaredSymbolInfoKind.Class, If(typeDeclaration.Kind() = SyntaxKind.InterfaceBlock, DeclaredSymbolInfoKind.Interface, If(typeDeclaration.Kind() = SyntaxKind.ModuleBlock, DeclaredSymbolInfoKind.Module, DeclaredSymbolInfoKind.Struct))), - GetAccessibility(container, typeDeclaration, typeDeclaration.BlockStatement.Modifiers), - typeDeclaration.BlockStatement.Identifier.Span, + GetAccessibility(container, typeDeclaration, blockStatement.Modifiers), + blockStatement.Identifier.Span, GetInheritanceNames(stringTable, typeDeclaration), - IsNestedType(typeDeclaration)) + IsNestedType(typeDeclaration), + typeParameterCount:=If(blockStatement.TypeParameterList?.Parameters.Count, 0)) End Function Protected Overrides Function GetEnumDeclarationInfo( @@ -176,15 +180,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName As String, fullyQualifiedContainerName As String) As DeclaredSymbolInfo + Dim enumStatement = enumDeclaration.EnumStatement + Return DeclaredSymbolInfo.Create( stringTable, - enumDeclaration.EnumStatement.Identifier.ValueText, Nothing, + enumStatement.Identifier.ValueText, Nothing, containerDisplayName, fullyQualifiedContainerName, - enumDeclaration.EnumStatement.Modifiers.Any(SyntaxKind.PartialKeyword), + enumStatement.Modifiers.Any(SyntaxKind.PartialKeyword), + enumStatement.AttributeLists.Any(), DeclaredSymbolInfoKind.Enum, - GetAccessibility(container, enumDeclaration, enumDeclaration.EnumStatement.Modifiers), - enumDeclaration.EnumStatement.Identifier.Span, + GetAccessibility(container, enumDeclaration, enumStatement.Modifiers), + enumStatement.Identifier.Span, ImmutableArray(Of String).Empty, IsNestedType(enumDeclaration)) End Function @@ -218,6 +225,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, constructor.Modifiers.Any(SyntaxKind.PartialKeyword), + constructor.AttributeLists.Any(), DeclaredSymbolInfoKind.Constructor, GetAccessibility(container, constructor, constructor.Modifiers), constructor.NewKeyword.Span, @@ -235,6 +243,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, delegateDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + delegateDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Delegate, GetAccessibility(container, delegateDecl, delegateDecl.Modifiers), delegateDecl.Identifier.Span, @@ -248,6 +257,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, isPartial:=False, + enumMember.AttributeLists.Any(), DeclaredSymbolInfoKind.EnumMember, Accessibility.Public, enumMember.Identifier.Span, @@ -261,6 +271,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, eventDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + eventDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Event, GetAccessibility(container, eventDecl, eventDecl.Modifiers), eventDecl.Identifier.Span, @@ -276,6 +287,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, funcDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + funcDecl.AttributeLists.Any(), If(isExtension, DeclaredSymbolInfoKind.ExtensionMethod, DeclaredSymbolInfoKind.Method), GetAccessibility(container, funcDecl, funcDecl.Modifiers), funcDecl.Identifier.Span, @@ -293,6 +305,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, propertyDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + propertyDecl.AttributeLists.Any(), DeclaredSymbolInfoKind.Property, GetAccessibility(container, propertyDecl, propertyDecl.Modifiers), propertyDecl.Identifier.Span, @@ -308,6 +321,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols containerDisplayName, fullyQualifiedContainerName, fieldDecl.Modifiers.Any(SyntaxKind.PartialKeyword), + fieldDecl.AttributeLists.Any(), If(fieldDecl.Modifiers.Any(Function(m) m.Kind() = SyntaxKind.ConstKeyword), DeclaredSymbolInfoKind.Constant, DeclaredSymbolInfoKind.Field), diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/VisualBasicTriviaFormatter.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/VisualBasicTriviaFormatter.vb index e37d011893896..0119c9972c42d 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/VisualBasicTriviaFormatter.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/VisualBasicTriviaFormatter.vb @@ -60,7 +60,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Return _newLine End Function - Protected Overrides Function GetLineColumnRuleBetween(trivia1 As SyntaxTrivia, existingWhitespaceBetween As LineColumnDelta, implicitLineBreak As Boolean, trivia2 As SyntaxTrivia) As LineColumnRule + Protected Overrides Function GetLineColumnRuleBetween(trivia1 As SyntaxTrivia, existingWhitespaceBetween As LineColumnDelta, implicitLineBreak As Boolean, trivia2 As SyntaxTrivia, cancellationToken As CancellationToken) As LineColumnRule ' line continuation If trivia2.Kind = SyntaxKind.LineContinuationTrivia Then 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/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb b/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb index 7d832b1a427e4..f2b17231d5975 100644 --- a/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb +++ b/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb @@ -3,7 +3,6 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports System.Threading Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.SemanticModelReuse @@ -16,7 +15,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SemanticModelReuse Friend Class VisualBasicSemanticModelReuseLanguageService Inherits AbstractSemanticModelReuseLanguageService(Of DeclarationStatementSyntax, - MethodBlockBaseSyntax, DeclarationStatementSyntax, AccessorBlockSyntax) @@ -55,20 +53,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SemanticModelReuse Return Nothing End Function - Protected Overrides Async Function TryGetSpeculativeSemanticModelWorkerAsync( - previousSemanticModel As SemanticModel, currentBodyNode As SyntaxNode, cancellationToken As CancellationToken) As Task(Of SemanticModel) + Protected Overrides Function TryGetSpeculativeSemanticModelWorker(previousSemanticModel As SemanticModel, previousBodyNode As SyntaxNode, currentBodyNode As SyntaxNode) As SemanticModel - Dim previousRoot = Await previousSemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(False) - Dim currentRoot = Await currentBodyNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(False) - - Dim previousBodyNode = TryCast(GetPreviousBodyNode(previousRoot, currentRoot, currentBodyNode), MethodBlockBaseSyntax) - If previousBodyNode Is Nothing Then + Dim previousMethodBlockBaseNode = TryCast(previousBodyNode, MethodBlockBaseSyntax) + If previousMethodBlockBaseNode Is Nothing Then Debug.Fail("Could not map current body to previous body, despite no top level changes") Return Nothing End If Dim speculativeModel As SemanticModel = Nothing - If previousSemanticModel.TryGetSpeculativeSemanticModelForMethodBody(previousBodyNode.BlockStatement.FullSpan.End, DirectCast(currentBodyNode, MethodBlockBaseSyntax), speculativeModel) Then + If previousSemanticModel.TryGetSpeculativeSemanticModelForMethodBody(previousMethodBlockBaseNode.BlockStatement.FullSpan.End, DirectCast(currentBodyNode, MethodBlockBaseSyntax), speculativeModel) Then Return speculativeModel End If 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..208f45861a712 --- /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(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 + 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..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 @@ -52,7 +53,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic cacheKey As ProjectId, filePath As String, options As ParseOptions, - text As ValueSource(Of TextAndVersion), + 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,12 +89,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Overrides Function TryGetText(ByRef text As SourceText) As Boolean + Public Overrides Function TryGetText( ByRef text As SourceText) As Boolean Return _info.TryGetText(text) End Function Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText - Return _info.TextSource.GetValue(cancellationToken).Text + Return _info.GetText(cancellationToken) End Function Public Overrides Function GetTextAsync(Optional cancellationToken As CancellationToken = Nothing) As Task(Of SourceText) diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb index 661d8c0775168..4a902b57aca50 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,8 @@ 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, + 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