diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 5352feb5461e7..7bdba491ceb3f 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -15,7 +15,7 @@ A minimal repro, with source-code provided, is ideal. Using [sharplab](https:// **Diagnostic Id**: -If this is a report about a bug in an analyzer, please include the diagnostic if possible (e.g. `"IDE0030"`). +If this is a report about a bug in an analyzer, please include the diagnostic ID and message if possible (e.g. `"IDE0030: Use coalesce expression"`). **Expected Behavior**: diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 72fb4cfb5b958..731a818a6f6c7 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -58,6 +58,7 @@ variables: - name: ArtifactServices.Drop.PAT value: $(dn-bot-devdiv-drop-rw-code-rw) - group: DotNet-Roslyn-Insertion-Variables + - group: AzureDevOps-Artifact-Feeds-Pats - name: BuildConfiguration value: release diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 47fb6533a7218..43ae84c0cc63f 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,6 +10,7 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | +| [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [In Progress](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | | [Partial properties](https://github.com/dotnet/csharplang/issues/6420) | [partial-properties](https://github.com/dotnet/roslyn/tree/features/partial-properties) | [In Progress](https://github.com/dotnet/roslyn/issues/73090) | [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv), [333fred](https://github.com/333fred) | [Cosifne](https://github.com/Cosifne) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | | Ref/unsafe in iterators/async | [RefInAsync](https://github.com/dotnet/roslyn/tree/features/RefInAsync) | [In Progress](https://github.com/dotnet/roslyn/issues/72662) | [jjonescz](https://github.com/jjonescz) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | (no IDE impact) | | | [Ref Struct Interfaces](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [ToddGrun](https://github.com/ToddGrun) | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md new file mode 100644 index 0000000000000..0b9cd956cdf28 --- /dev/null +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -0,0 +1,27 @@ +# This document lists known breaking changes in Roslyn after .NET 8 all the way to .NET 9. + +## Iterators introduce safe context in C# 13 and newer + +***Introduced in Visual Studio 2022 version 17.11*** + +Although the language spec states that iterators introduce a safe context, Roslyn does not implement that in C# 12 and lower. +This will change in C# 13 as part of [a feature which allows unsafe code in iterators](https://github.com/dotnet/roslyn/issues/72662). +The change does not break normal scenarios as it was disallowed to use unsafe constructs directly in iterators anyway. +However, it can break scenarios where an unsafe context was previously inherited into nested local functions, for example: + +```cs +unsafe class C // unsafe context +{ + System.Collections.Generic.IEnumerable M() // an iterator + { + yield return 1; + local(); + void local() + { + int* p = null; // allowed in C# 12; error in C# 13 + } + } +} +``` + +You can work around the break simply by adding the `unsafe` modifier to the local function. diff --git a/docs/compilers/CSharp/Warnversion Warning Waves.md b/docs/compilers/CSharp/Warnversion Warning Waves.md index 65d25e8b1b528..f80c80e658838 100644 --- a/docs/compilers/CSharp/Warnversion Warning Waves.md +++ b/docs/compilers/CSharp/Warnversion Warning Waves.md @@ -13,6 +13,14 @@ In a typical project, this setting is controlled by the `AnalysisLevel` property which determines the `WarningLevel` property (passed to the `Csc` task). For more information on `AnalysisLevel`, see https://devblogs.microsoft.com/dotnet/automatically-find-latent-bugs-in-your-code-with-net-5/ +## Warning level 9 + +The compiler shipped with .NET 9 (the C# 13 compiler) contains the following warnings which are reported only under `/warn:9` or higher. + +| Warning ID | Description | +|------------|-------------| +| CS9237 | ['yield return' should not be used in the body of a lock statement](https://github.com/dotnet/roslyn/issues/72443) | + ## Warning level 8 The compiler shipped with .NET 8 (the C# 12 compiler) contains the following warnings which are reported only under `/warn:8` or higher. diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md index 43408387bddba..0482960a9d56c 100644 --- a/docs/contributing/Building, Debugging, and Testing on Windows.md +++ b/docs/contributing/Building, Debugging, and Testing on Windows.md @@ -86,7 +86,7 @@ give it a try. ### Deploying with command-line (recommended method) You can build and deploy with the following command: -`.\Build.cmd -Configuration Release -deployExtensions -launch`. +`.\Build.cmd -Restore -Configuration Release -deployExtensions -launch`. Then you can launch the `RoslynDev` hive with `devenv /rootSuffix RoslynDev`. diff --git a/docs/contributing/Target Framework Strategy.md b/docs/contributing/Target Framework Strategy.md index a6545072f1920..7395188691ce2 100644 --- a/docs/contributing/Target Framework Strategy.md +++ b/docs/contributing/Target Framework Strategy.md @@ -26,6 +26,7 @@ Projects in our repository should include the following values in `` setting. Instead our repo uses the above values and when inside source build or VMR our properties are initialized with arcade properties. @@ -58,7 +59,7 @@ This problem primarily comes from our use of polyfill APIs. To avoid this we emp This comes up in two forms: -### Pattern for types +### Pattern for types When creating a polyfill for a type use the `#if !NET...` to declare the type and in the `#else` use a `TypeForwardedTo` for the actual type. @@ -99,6 +100,13 @@ When creating a polyfill for an extension use the `#if NET...` to declare the ex #endif ``` +## Transitioning to new .NET SDK +As the .NET team approaches releasing a new .NET SDK the Roslyn team will begin using preview versions of that SDK in our build. This will often lead to test failures in our CI system due to underlying behavior changes in the runtime. These failures will often not show up when running in Visual Studio due to the way the runtime for the test runner is chosen. +To ensure we have a simple developer environment such project should be moved to to the `$(NetRoslynNext)` target framework. That ensures the new runtime is loaded when running tests locally. + +When the .NET SDK RTMs and Roslyn adopts it all occurrences of `$(NetRoslynNext)` will be moved to simply `$(NetRoslyn)`. + +**DO NOT** include both `$(NetRoslyn)` and `$(NetRoslynNext)` in the same project unless there is a very specific reason that both tests are adding value. The most common case is that the runtime has changed behavior and we simply need to update our baselines to match it. Adding extra TFMs for this just increases test time for very little gain. diff --git a/docs/wiki/EnC-Supported-Edits.md b/docs/wiki/EnC-Supported-Edits.md index c05d40b1121ce..39e7f453d42c2 100644 --- a/docs/wiki/EnC-Supported-Edits.md +++ b/docs/wiki/EnC-Supported-Edits.md @@ -50,3 +50,24 @@ This document captures the current state. Potential future improvements in this | Edit a member referencing an embedded interop type | - | | Edit a member with On Error or Resume statements | Specific to Visual Basic | | Edit a member containing an Aggregate, Group By, Simple Join, or Group Join LINQ query clause | Specific to Visual Basic | +| Edit in a solution containing projects that specify `*` in `AssemblyVersionAttribute`, e.g. `[assembly: AssemblyVersion("1.0.*")`]. | See [workaround](#projects-with-variable-assembly-versions) below. | + +### Projects with variable assembly versions + +Hot Reload and Edit & Continue are not compatible with using `*` in `AssemblyVersionAttribute` value. Presence of `*` in the version means that the compiler generates a new version +every build based on the current time. The build then produces non-reproducible, non-deterministic outputs (see [Reproducible Builds](https://reproducible-builds.org)). + +It is thus highly recommended to use alternative versioning methods, such as [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning), that derive the assembly version +from the HEAD commit SHA (for git repositories). + +> To enable generating commit SHA based assembly versions using `Nerdbank.GitVersioning` package, specify `{ "assemblyVersion" : {"precision": "revision"} }` setting in `version.json`. + +If you prefer to keep using `*` in `AssemblyVersionAttribute` it is recommended to use conditional compilation directive to only apply such version to Release builds like so: + +```C# +#if DEBUG +[assembly: AssemblyVersion("1.0.0.0")] +#else +[assembly: AssemblyVersion("1.0.*")] +#endif +``` diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 8adaa8740c2ac..422156efd5e75 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -5,6 +5,7 @@ 8.0.0-preview.23468.1 1.1.2-beta1.24121.1 0.1.187-beta + <_BasicReferenceAssembliesVersion>1.7.2 4.8.0-3.final 17.10.72-preview @@ -56,7 +57,7 @@ Visual Studio --> - + @@ -99,7 +100,7 @@ - + @@ -138,7 +139,7 @@ - + @@ -293,13 +294,14 @@ - - - - - - - + + + + + + + + + - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 97d63d81605e0..369ff10342899 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -110,14 +110,14 @@ - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 15eea424d3b2dd25a5c0b10e8adc8aeed50129a1 - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 15eea424d3b2dd25a5c0b10e8adc8aeed50129a1 @@ -144,9 +144,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 15eea424d3b2dd25a5c0b10e8adc8aeed50129a1 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/common/build.cmd b/eng/common/build.cmd new file mode 100644 index 0000000000000..99daf368abae7 --- /dev/null +++ b/eng/common/build.cmd @@ -0,0 +1,3 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" +exit /b %ErrorLevel% diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 33a6f2d0e2481..438f9920c43e4 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -19,6 +19,7 @@ Param( [switch] $pack, [switch] $publish, [switch] $clean, + [switch][Alias('pb')]$productBuild, [switch][Alias('bl')]$binaryLog, [switch][Alias('nobl')]$excludeCIBinarylog, [switch] $ci, @@ -58,6 +59,7 @@ function Print-Usage() { Write-Host " -sign Sign build outputs" Write-Host " -publish Publish artifacts (e.g. symbols)" Write-Host " -clean Clean the solution" + Write-Host " -productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" Write-Host "" Write-Host "Advanced settings:" @@ -120,6 +122,7 @@ function Build { /p:Deploy=$deploy ` /p:Test=$test ` /p:Pack=$pack ` + /p:DotNetBuildRepo=$productBuild ` /p:IntegrationTest=$integrationTest ` /p:PerformanceTest=$performanceTest ` /p:Sign=$sign ` diff --git a/eng/common/build.sh b/eng/common/build.sh index 50af40cdd2ce6..ac1ee8620cd2a 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -22,6 +22,9 @@ usage() echo " --sourceBuild Source-build the solution (short: -sb)" echo " Will additionally trigger the following actions: --restore, --build, --pack" echo " If --configuration is not set explicitly, will also set it to 'Release'" + echo " --productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" + echo " Will additionally trigger the following actions: --restore, --build, --pack" + echo " If --configuration is not set explicitly, will also set it to 'Release'" echo " --rebuild Rebuild solution" echo " --test Run all unit tests in the solution (short: -t)" echo " --integrationTest Run all integration tests in the solution" @@ -59,6 +62,7 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" restore=false build=false source_build=false +product_build=false rebuild=false test=false integration_test=false @@ -105,7 +109,7 @@ while [[ $# > 0 ]]; do -binarylog|-bl) binary_log=true ;; - -excludeCIBinarylog|-nobl) + -excludecibinarylog|-nobl) exclude_ci_binary_log=true ;; -pipelineslog|-pl) @@ -126,6 +130,13 @@ while [[ $# > 0 ]]; do -sourcebuild|-sb) build=true source_build=true + product_build=true + restore=true + pack=true + ;; + -productBuild|-pb) + build=true + product_build=true restore=true pack=true ;; @@ -219,7 +230,9 @@ function Build { /p:RepoRoot="$repo_root" \ /p:Restore=$restore \ /p:Build=$build \ + /p:DotNetBuildRepo=$product_build \ /p:ArcadeBuildFromSource=$source_build \ + /p:DotNetBuildSourceOnly=$source_build \ /p:Rebuild=$rebuild \ /p:Test=$test \ /p:Pack=$pack \ diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml new file mode 100644 index 0000000000000..dc3bd560a50e2 --- /dev/null +++ b/eng/common/core-templates/job/job.yml @@ -0,0 +1,266 @@ +parameters: +# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + cancelTimeoutInMinutes: '' + condition: '' + container: '' + continueOnError: false + dependsOn: '' + displayName: '' + pool: '' + steps: [] + strategy: '' + timeoutInMinutes: '' + variables: [] + workspace: '' + templateContext: {} + +# Job base template specific parameters + # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md + # publishing defaults + artifacts: '' + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishBuildAssets: false + enablePublishTestResults: false + enablePublishUsingPipelines: false + enableBuildRetry: false + disableComponentGovernance: '' + componentGovernanceIgnoreDirectories: '' + mergeTestResults: false + testRunTitle: '' + testResultsFormat: '' + name: '' + preSteps: [] + artifactPublishSteps: [] + runAsPublic: false + +# Sbom related params + enableSbom: true + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + +# 1es specific parameters + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.name }} + + ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: + cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} + + ${{ if ne(parameters.condition, '') }}: + condition: ${{ parameters.condition }} + + ${{ if ne(parameters.container, '') }}: + container: ${{ parameters.container }} + + ${{ if ne(parameters.continueOnError, '') }}: + continueOnError: ${{ parameters.continueOnError }} + + ${{ if ne(parameters.dependsOn, '') }}: + dependsOn: ${{ parameters.dependsOn }} + + ${{ if ne(parameters.displayName, '') }}: + displayName: ${{ parameters.displayName }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + + ${{ if ne(parameters.strategy, '') }}: + strategy: ${{ parameters.strategy }} + + ${{ if ne(parameters.timeoutInMinutes, '') }}: + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + + ${{ if ne(parameters.templateContext, '') }}: + templateContext: ${{ parameters.templateContext }} + + variables: + - ${{ if ne(parameters.enableTelemetry, 'false') }}: + - name: DOTNET_CLI_TELEMETRY_PROFILE + value: '$(Build.Repository.Uri)' + - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: + - name: EnableRichCodeNavigation + value: 'true' + # Retry signature validation up to three times, waiting 2 seconds between attempts. + # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures + - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY + value: 3,2000 + - ${{ each variable in parameters.variables }}: + # handle name-value variable syntax + # example: + # - name: [key] + # value: [value] + - ${{ if ne(variable.name, '') }}: + - name: ${{ variable.name }} + value: ${{ variable.value }} + + # handle variable groups + - ${{ if ne(variable.group, '') }}: + - group: ${{ variable.group }} + + # handle template variable syntax + # example: + # - template: path/to/template.yml + # parameters: + # [key]: [value] + - ${{ if ne(variable.template, '') }}: + - template: ${{ variable.template }} + ${{ if ne(variable.parameters, '') }}: + parameters: ${{ variable.parameters }} + + # handle key-value variable syntax. + # example: + # - [key]: [value] + - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: + - ${{ each pair in variable }}: + - name: ${{ pair.key }} + value: ${{ pair.value }} + + # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds + - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: DotNet-HelixApi-Access + + ${{ if ne(parameters.workspace, '') }}: + workspace: ${{ parameters.workspace }} + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.preSteps, '') }}: + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + env: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + + - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: + - task: NuGetAuthenticate@1 + + - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: + - task: DownloadPipelineArtifact@2 + inputs: + buildType: current + artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} + targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} + itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} + + - ${{ each step in parameters.steps }}: + - ${{ step }} + + - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: + - task: RichCodeNavIndexer@0 + displayName: RichCodeNav Upload + inputs: + languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} + environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'internal') }} + richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin + uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} + continueOnError: true + + - template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ if eq(parameters.disableComponentGovernance, '') }}: + ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: + disableComponentGovernance: false + ${{ else }}: + disableComponentGovernance: true + ${{ else }}: + disableComponentGovernance: ${{ parameters.disableComponentGovernance }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: MicroBuildCleanup@1 + displayName: Execute Microbuild cleanup tasks + condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + env: + TeamName: $(_TeamName) + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + PackageVersion: ${{ parameters.packageVersion}} + BuildDropPath: ${{ parameters.buildDropPath }} + IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + publishArtifacts: false + + # Publish test results + - ${{ if and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')) }}: + - ${{ if eq(parameters.testResultsFormat, 'xunit') }}: + - task: PublishTestResults@2 + displayName: Publish XUnit Test Results + inputs: + testResultsFormat: 'xUnit' + testResultsFiles: '*.xml' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + - ${{ if eq(parameters.testResultsFormat, 'vstest') }}: + - task: PublishTestResults@2 + displayName: Publish TRX Test Results + inputs: + testResultsFormat: 'VSTest' + testResultsFiles: '*.trx' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + + # gather artifacts + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - task: CopyFiles@2 + displayName: Gather binaries for publish to artifacts + inputs: + SourceFolder: 'artifacts/bin' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' + - task: CopyFiles@2 + displayName: Gather packages for publish to artifacts + inputs: + SourceFolder: 'artifacts/packages' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log/$(_BuildConfig)' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - task: CopyFiles@2 + displayName: Gather buildconfiguration for build retry + inputs: + SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' + + - ${{ each step in parameters.artifactPublishSteps }}: + - ${{ step }} diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml new file mode 100644 index 0000000000000..00feec8ebbc3a --- /dev/null +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -0,0 +1,121 @@ +parameters: + # Optional: dependencies of the job + dependsOn: '' + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: '' + + CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex + GithubPat: $(BotAccount-dotnet-bot-repo-PAT) + + SourcesDirectory: $(Build.SourcesDirectory) + CreatePr: true + AutoCompletePr: false + ReusePr: true + UseLfLineEndings: true + UseCheckedInLocProjectJson: false + SkipLocProjectJsonGeneration: false + LanguageSet: VS_Main_Languages + LclSource: lclFilesInRepo + LclPackageId: '' + RepoType: gitHub + GitHubOrg: dotnet + MirrorRepo: '' + MirrorBranch: main + condition: '' + JobNameSuffix: '' + is1ESPipeline: '' +jobs: +- job: OneLocBuild${{ parameters.JobNameSuffix }} + + dependsOn: ${{ parameters.dependsOn }} + + displayName: OneLocBuild${{ parameters.JobNameSuffix }} + + variables: + - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat + - name: _GenerateLocProjectArguments + value: -SourcesDirectory ${{ parameters.SourcesDirectory }} + -LanguageSet "${{ parameters.LanguageSet }}" + -CreateNeutralXlfs + - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: + - name: _GenerateLocProjectArguments + value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: + - task: Powershell@2 + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + arguments: $(_GenerateLocProjectArguments) + displayName: Generate LocProject.json + condition: ${{ parameters.condition }} + + - task: OneLocBuild@2 + displayName: OneLocBuild + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + locProj: eng/Localize/LocProject.json + outDir: $(Build.ArtifactStagingDirectory) + lclSource: ${{ parameters.LclSource }} + lclPackageId: ${{ parameters.LclPackageId }} + isCreatePrSelected: ${{ parameters.CreatePr }} + isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} + ${{ if eq(parameters.CreatePr, true) }}: + isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + isShouldReusePrSelected: ${{ parameters.ReusePr }} + packageSourceAuth: patAuth + patVariable: ${{ parameters.CeapexPat }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + repoType: ${{ parameters.RepoType }} + gitHubPatVariable: "${{ parameters.GithubPat }}" + ${{ if ne(parameters.MirrorRepo, '') }}: + isMirrorRepoSelected: true + gitHubOrganization: ${{ parameters.GitHubOrg }} + mirrorRepo: ${{ parameters.MirrorRepo }} + mirrorBranch: ${{ parameters.MirrorBranch }} + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Localization Files + pathToPublish: '$(Build.ArtifactStagingDirectory)/loc' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish LocProject.json + pathToPublish: '$(Build.SourcesDirectory)/eng/Localize/' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml new file mode 100644 index 0000000000000..8fe9299542c53 --- /dev/null +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -0,0 +1,172 @@ +parameters: + configuration: 'Debug' + + # Optional: condition for the job to run + condition: '' + + # Optional: 'true' if future jobs should run even if this job fails + continueOnError: false + + # Optional: dependencies of the job + dependsOn: '' + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: {} + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishUsingPipelines: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishAssetsImmediately: false + + artifactsPublishingAdditionalParameters: '' + + signingValidationAdditionalParameters: '' + + is1ESPipeline: '' + +jobs: +- job: Asset_Registry_Publish + + dependsOn: ${{ parameters.dependsOn }} + timeoutInMinutes: 150 + + ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + displayName: Publish Assets + ${{ else }}: + displayName: Publish to Build Asset Registry + + variables: + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: Publish-Build-Assets + - group: AzureDevOps-Artifact-Feeds-Pats + - name: runCodesignValidationInjection + value: false + # unconditional - needed for logs publishing (redactor tool version) + - template: /eng/common/core-templates/post-build/common-variables.yml + + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - checkout: self + fetchDepth: 3 + clean: true + + - task: DownloadBuildArtifacts@0 + displayName: Download artifact + inputs: + artifactName: AssetManifests + downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Build Assets + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' + /p:BuildAssetRegistryToken=$(MaestroAccessToken) + /p:MaestroApiEndpoint=https://maestro.dot.net + /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} + /p:OfficialBuildId=$(Build.BuildNumber) + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: powershell@2 + displayName: Create ReleaseConfigs Artifact + inputs: + targetType: inline + script: | + New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force + $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" + Add-Content -Path $filePath -Value $(BARBuildId) + Add-Content -Path $filePath -Value "$(DefaultChannels)" + Add-Content -Path $filePath -Value $(IsStableBuild) + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish ReleaseConfigs Artifact + pathToPublish: '$(Build.StagingDirectory)/ReleaseConfigs' + publishLocation: Container + artifactName: ReleaseConfigs + + - task: powershell@2 + displayName: Check if SymbolPublishingExclusionsFile.txt exists + inputs: + targetType: inline + script: | + $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + if(Test-Path -Path $symbolExclusionfile) + { + Write-Host "SymbolExclusionFile exists" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" + } + else{ + Write-Host "Symbols Exclusion file does not exist" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" + } + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SymbolPublishingExclusionsFile Artifact + condition: eq(variables['SymbolExclusionFile'], 'true') + pathToPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' + publishLocation: Container + artifactName: ReleaseConfigs + + - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion 3 + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml new file mode 100644 index 0000000000000..c0ce4b3c86186 --- /dev/null +++ b/eng/common/core-templates/job/source-build.yml @@ -0,0 +1,80 @@ +parameters: + # This template adds arcade-powered source-build to CI. The template produces a server job with a + # default ID 'Source_Build_Complete' to put in a dependency list if necessary. + + # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. + jobNamePrefix: 'Source_Build' + + # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for + # managed-only repositories. This is an object with these properties: + # + # name: '' + # The name of the job. This is included in the job ID. + # targetRID: '' + # The name of the target RID to use, instead of the one auto-detected by Arcade. + # nonPortable: false + # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than + # linux-x64), and compiling against distro-provided packages rather than portable ones. + # skipPublishValidation: false + # Disables publishing validation. By default, a check is performed to ensure no packages are + # published by source-build. + # container: '' + # A container to use. Runs in docker. + # pool: {} + # A pool to use. Runs directly on an agent. + # buildScript: '' + # Specifies the build script to invoke to perform the build in the repo. The default + # './build.sh' should work for typical Arcade repositories, but this is customizable for + # difficult situations. + # jobProperties: {} + # A list of job properties to inject at the top level, for potential extensibility beyond + # container and pool. + platform: {} + + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} + displayName: Source-Build (${{ parameters.platform.name }}) + + ${{ each property in parameters.platform.jobProperties }}: + ${{ property.key }}: ${{ property.value }} + + ${{ if ne(parameters.platform.container, '') }}: + container: ${{ parameters.platform.container }} + + ${{ if eq(parameters.platform.pool, '') }}: + # The default VM host AzDO pool. This should be capable of running Docker containers: almost all + # source-build builds run in Docker, including the default managed platform. + # /eng/common/core-templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic + ${{ if eq(parameters.is1ESPipeline, 'true') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals build.ubuntu.2004.amd64 + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + image: 1es-mariner-2 + os: linux + ${{ else }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 + ${{ if ne(parameters.platform.pool, '') }}: + pool: ${{ parameters.platform.pool }} + + workspace: + clean: all + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + platform: ${{ parameters.platform }} diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml new file mode 100644 index 0000000000000..f1938eec10203 --- /dev/null +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -0,0 +1,91 @@ +parameters: + runAsPublic: false + sourceIndexUploadPackageVersion: 2.0.0-20240502.12 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2 + sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json + sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" + preSteps: [] + binlogPath: artifacts/log/Debug/Build.binlog + condition: '' + dependsOn: '' + pool: '' + is1ESPipeline: '' + +jobs: +- job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} + variables: + - name: SourceIndexUploadPackageVersion + value: ${{ parameters.sourceIndexUploadPackageVersion }} + - name: SourceIndexProcessBinlogPackageVersion + value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }} + - name: SourceIndexPackageSource + value: ${{ parameters.sourceIndexPackageSource }} + - name: BinlogPath + value: ${{ parameters.binlogPath }} + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: windows.vs2022.amd64.open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - task: UseDotNet@2 + displayName: Use .NET 8 SDK + inputs: + packageType: sdk + version: 8.0.x + installationPath: $(Agent.TempDirectory)/dotnet + workingDirectory: $(Agent.TempDirectory) + + - script: | + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + displayName: Download Tools + # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. + workingDirectory: $(Agent.TempDirectory) + + - script: ${{ parameters.sourceIndexBuildCommand }} + displayName: Build Repository + + - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output + displayName: Process Binlog into indexable sln + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: AzureCLI@2 + displayName: Get stage 1 auth token + inputs: + azureSubscription: 'SourceDotNet Stage1 Publish' + addSpnToEnvironment: true + scriptType: 'ps' + scriptLocation: 'inlineScript' + inlineScript: | + echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken" + echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId" + + - script: | + echo "Client ID: $(ARM_CLIENT_ID)" + echo "ID Token: $(ARM_ID_TOKEN)" + echo "Tenant ID: $(ARM_TENANT_ID)" + az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN) + displayName: "Login to Azure" + + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1 + displayName: Upload stage1 artifacts to source index \ No newline at end of file diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml new file mode 100644 index 0000000000000..f2144252cc65c --- /dev/null +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -0,0 +1,33 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + # Optional: if specified, restore and use this version of Guardian instead of the default. + overrideGuardianVersion: '' + is1ESPipeline: '' + +jobs: +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishTestResults: false + enablePublishBuildAssets: false + enablePublishUsingPipelines: false + enableTelemetry: true + + variables: + - group: Publish-Build-Assets + # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in + # sync with the packages.config file. + - name: DefaultGuardianVersion + value: 0.109.0 + - name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + - name: GuardianVersion + value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} + + jobs: ${{ parameters.jobs }} + diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml new file mode 100644 index 0000000000000..ea69be4341c62 --- /dev/null +++ b/eng/common/core-templates/jobs/jobs.yml @@ -0,0 +1,119 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: Enable publishing using release pipelines + enablePublishUsingPipelines: false + + # Optional: Enable running the source-build jobs to build repo from source + enableSourceBuild: false + + # Optional: Parameters for source-build template. + # See /eng/common/core-templates/jobs/source-build.yml for options + sourceBuildParameters: [] + + graphFileGeneration: + # Optional: Enable generating the graph files at the end of the build + enabled: false + # Optional: Include toolset dependencies in the generated graph files + includeToolset: false + + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + + # Optional: Override automatically derived dependsOn value for "publish build assets" job + publishBuildAssetsDependsOn: '' + + # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. + publishAssetsImmediately: false + + # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) + artifactsPublishingAdditionalParameters: '' + signingValidationAdditionalParameters: '' + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + enableSourceIndex: false + sourceIndexParams: {} + + artifacts: {} + is1ESPipeline: '' + +# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, +# and some (Microbuild) should only be applied to non-PR cases for internal builds. + +jobs: +- ${{ each job in parameters.jobs }}: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + + - ${{ else }}: + - template: /eng/common/templates/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + +- ${{ if eq(parameters.enableSourceBuild, true) }}: + - template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + allCompletedJobId: Source_Build_Complete + ${{ each parameter in parameters.sourceBuildParameters }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if eq(parameters.enableSourceIndex, 'true') }}: + - template: ../job/source-index-stage1.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + runAsPublic: ${{ parameters.runAsPublic }} + ${{ each parameter in parameters.sourceIndexParams }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: + - template: ../job/publish-build-assets.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + continueOnError: ${{ parameters.continueOnError }} + dependsOn: + - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.publishBuildAssetsDependsOn }}: + - ${{ job.job }} + - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.jobs }}: + - ${{ job.job }} + - ${{ if eq(parameters.enableSourceBuild, true) }}: + - Source_Build_Complete + + runAsPublic: ${{ parameters.runAsPublic }} + publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} + publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} + enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} + artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} + signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml new file mode 100644 index 0000000000000..d8e5d00852268 --- /dev/null +++ b/eng/common/core-templates/jobs/source-build.yml @@ -0,0 +1,50 @@ +parameters: + # This template adds arcade-powered source-build to CI. A job is created for each platform, as + # well as an optional server job that completes when all platform jobs complete. + + # The name of the "join" job for all source-build platforms. If set to empty string, the job is + # not included. Existing repo pipelines can use this job depend on all source-build jobs + # completing without maintaining a separate list of every single job ID: just depend on this one + # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. + allCompletedJobId: '' + + # See /eng/common/core-templates/job/source-build.yml + jobNamePrefix: 'Source_Build' + + # This is the default platform provided by Arcade, intended for use by a managed-only repo. + defaultManagedPlatform: + name: 'Managed' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + + # Defines the platforms on which to run build jobs. One job is created for each platform, and the + # object in this array is sent to the job template as 'platform'. If no platforms are specified, + # one job runs on 'defaultManagedPlatform'. + platforms: [] + + is1ESPipeline: '' + +jobs: + +- ${{ if ne(parameters.allCompletedJobId, '') }}: + - job: ${{ parameters.allCompletedJobId }} + displayName: Source-Build Complete + pool: server + dependsOn: + - ${{ each platform in parameters.platforms }}: + - ${{ parameters.jobNamePrefix }}_${{ platform.name }} + - ${{ if eq(length(parameters.platforms), 0) }}: + - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} + +- ${{ each platform in parameters.platforms }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ platform }} + +- ${{ if eq(length(parameters.platforms), 0) }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ parameters.defaultManagedPlatform }} diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml new file mode 100644 index 0000000000000..b9ede10bf099a --- /dev/null +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -0,0 +1,24 @@ +variables: + - group: Publish-Build-Assets + + # Whether the build is internal or not + - name: IsInternalBuild + value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} + + # Default Maestro++ API Endpoint and API Version + - name: MaestroApiEndPoint + value: "https://maestro.dot.net" + - name: MaestroApiAccessToken + value: $(MaestroAccessToken) + - name: MaestroApiVersion + value: "2020-02-20" + + - name: SourceLinkCLIVersion + value: 3.0.0 + - name: SymbolToolVersion + value: 1.0.1 + - name: BinlogToolVersion + value: 1.0.11 + + - name: runCodesignValidationInjection + value: false diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml new file mode 100644 index 0000000000000..865bc1ecb4f0f --- /dev/null +++ b/eng/common/core-templates/post-build/post-build.yml @@ -0,0 +1,314 @@ +parameters: + # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. + # Publishing V1 is no longer supported + # Publishing V2 is no longer supported + # Publishing V3 is the default + - name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + + - name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + + - name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + + - name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + + - name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + + - name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + + - name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + + - name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + + - name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + + # These parameters let the user customize the call to sdk-task.ps1 for publishing + # symbols & general artifacts as well as for signing validation + - name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + + - name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + + - name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + + # Which stages should finish execution before post-build stages start + - name: validateDependsOn + type: object + default: + - build + + - name: publishDependsOn + type: object + default: + - Validate + + # Optional: Call asset publishing rather than running in a separate stage + - name: publishAssetsImmediately + type: boolean + default: false + + - name: is1ESPipeline + type: boolean + default: false + +stages: +- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + - stage: Validate + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Validate Build Assets + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: NuGet Validation + condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ + + - job: + displayName: Signing Validation + condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + itemPattern: | + ** + !**/Microsoft.SourceBuild.Intermediate.*.nupkg + + # This is necessary whenever we want to publish/restore to an AzDO private feed + # Since sdk-task.ps1 tries to restore packages we need to do this authentication here + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) + + - job: + displayName: SourceLink Validation + condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Blob Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: BlobArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 + arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ + -ExtractPath $(Agent.BuildDirectory)/Extract/ + -GHRepoName $(Build.Repository.Name) + -GHCommit $(Build.SourceVersion) + -SourcelinkCliVersion $(SourceLinkCLIVersion) + continueOnError: true + +- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: + - stage: publish_using_darc + ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + dependsOn: ${{ parameters.publishDependsOn }} + ${{ else }}: + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Publish using Darc + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: Publish Using Darc + timeoutInMinutes: 120 + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + ${{ else }}: + name: NetCore1ESPool-Publishing-Internal + demands: ImageOverride -equals windows.vs2019.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml new file mode 100644 index 0000000000000..8d56b5726793f --- /dev/null +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -0,0 +1,74 @@ +parameters: + BARBuildId: '' + PromoteToChannelIds: '' + is1ESPipeline: '' + +steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Release Configs + inputs: + buildType: current + artifactName: ReleaseConfigs + checkDownloadedFiles: true + + - task: PowerShell@2 + name: setReleaseVars + displayName: Set Release Configs Vars + inputs: + targetType: inline + pwsh: true + script: | + try { + if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { + $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt + + $BarId = $Content | Select -Index 0 + $Channels = $Content | Select -Index 1 + $IsStableBuild = $Content | Select -Index 2 + + $AzureDevOpsProject = $Env:System_TeamProject + $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId + $AzureDevOpsBuildId = $Env:Build_BuildId + } + else { + $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" + + $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' + $apiHeaders.Add('Accept', 'application/json') + $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") + + $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + + $BarId = $Env:BARBuildId + $Channels = $Env:PromoteToMaestroChannels -split "," + $Channels = $Channels -join "][" + $Channels = "[$Channels]" + + $IsStableBuild = $buildInfo.stable + $AzureDevOpsProject = $buildInfo.azureDevOpsProject + $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId + $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId + } + + Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" + Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" + Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" + + Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" + Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" + Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" + } + catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 + } + env: + MAESTRO_API_TOKEN: $(MaestroApiAccessToken) + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} diff --git a/eng/common/core-templates/post-build/trigger-subscription.yml b/eng/common/core-templates/post-build/trigger-subscription.yml new file mode 100644 index 0000000000000..da669030daf6e --- /dev/null +++ b/eng/common/core-templates/post-build/trigger-subscription.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Triggering subscriptions + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1 + arguments: -SourceRepo $(Build.Repository.Uri) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/add-build-to-channel.yml b/eng/common/core-templates/steps/add-build-to-channel.yml new file mode 100644 index 0000000000000..f67a210d62f3e --- /dev/null +++ b/eng/common/core-templates/steps/add-build-to-channel.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Add Build to Channel + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 + arguments: -BuildId $(BARBuildId) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroApiAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/component-governance.yml b/eng/common/core-templates/steps/component-governance.yml new file mode 100644 index 0000000000000..df449a34c1120 --- /dev/null +++ b/eng/common/core-templates/steps/component-governance.yml @@ -0,0 +1,14 @@ +parameters: + disableComponentGovernance: false + componentGovernanceIgnoreDirectories: '' + is1ESPipeline: false + +steps: +- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: + - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" + displayName: Set skipComponentGovernanceDetection variable +- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: + - task: ComponentGovernanceComponentDetection@0 + continueOnError: true + inputs: + ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml new file mode 100644 index 0000000000000..d938b60e1bb53 --- /dev/null +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -0,0 +1,54 @@ +# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. +# PackageName - The name of the package this SBOM represents. +# PackageVersion - The version of the package this SBOM represents. +# ManifestDirPath - The path of the directory where the generated manifest files will be placed +# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. + +parameters: + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageName: '.NET' + ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom + IgnoreDirectories: '' + sbomContinueOnError: true + is1ESPipeline: false + # disable publishArtifacts if some other step is publishing the artifacts (like job.yml). + publishArtifacts: true + +steps: +- task: PowerShell@2 + displayName: Prep for SBOM generation in (Non-linux) + condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) + inputs: + filePath: ./eng/common/generate-sbom-prep.ps1 + arguments: ${{parameters.manifestDirPath}} + +# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 +- script: | + chmod +x ./eng/common/generate-sbom-prep.sh + ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} + displayName: Prep for SBOM generation in (Linux) + condition: eq(variables['Agent.Os'], 'Linux') + continueOnError: ${{ parameters.sbomContinueOnError }} + +- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: 'Generate SBOM manifest' + continueOnError: ${{ parameters.sbomContinueOnError }} + inputs: + PackageName: ${{ parameters.packageName }} + BuildDropPath: ${{ parameters.buildDropPath }} + PackageVersion: ${{ parameters.packageVersion }} + ManifestDirPath: ${{ parameters.manifestDirPath }} + ${{ if ne(parameters.IgnoreDirectories, '') }}: + AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' + +- ${{ if eq(parameters.publishArtifacts, 'true')}}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SBOM manifest + continueOnError: ${{parameters.sbomContinueOnError}} + targetPath: '${{ parameters.manifestDirPath }}' + artifactName: $(ARTIFACT_NAME) + diff --git a/eng/common/core-templates/steps/publish-build-artifacts.yml b/eng/common/core-templates/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..f24ce346684e6 --- /dev/null +++ b/eng/common/core-templates/steps/publish-build-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false +- name: args + type: object + default: {} +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml new file mode 100644 index 0000000000000..8c5ea77b586d2 --- /dev/null +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -0,0 +1,59 @@ +parameters: + StageLabel: '' + JobLabel: '' + CustomSensitiveDataList: '' + # A default - in case value from eng/common/core-templates/post-build/common-variables.yml is not passed + BinlogToolVersion: '1.0.11' + is1ESPipeline: false + +steps: +- task: Powershell@2 + displayName: Prepare Binlogs to Upload + inputs: + targetType: inline + script: | + New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + continueOnError: true + condition: always() + +- task: PowerShell@2 + displayName: Redact Logs + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml + # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # If the file exists - sensitive data for redaction will be sourced from it + # (single entry per line, lines starting with '# ' are considered comments and skipped) + arguments: -InputPath '$(Build.SourcesDirectory)/PostBuildLogs' + -BinlogToolVersion ${{parameters.BinlogToolVersion}} + -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + '$(publishing-dnceng-devdiv-code-r-build-re)' + '$(MaestroAccessToken)' + '$(dn-bot-all-orgs-artifact-feeds-rw)' + '$(akams-client-id)' + '$(akams-client-secret)' + '$(microsoft-symbol-server-pat)' + '$(symweb-symbol-server-pat)' + '$(dn-bot-all-orgs-build-rw-code-rw)' + ${{parameters.CustomSensitiveDataList}} + continueOnError: true + condition: always() + +- task: CopyFiles@2 + displayName: Gather post build logs + inputs: + SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + +- template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + publishLocation: Container + artifactName: PostBuildLogs + continueOnError: true + condition: always() diff --git a/eng/common/core-templates/steps/publish-pipeline-artifacts.yml b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..2efec04dc2c16 --- /dev/null +++ b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/core-templates/steps/retain-build.yml b/eng/common/core-templates/steps/retain-build.yml new file mode 100644 index 0000000000000..83d97a26a01ff --- /dev/null +++ b/eng/common/core-templates/steps/retain-build.yml @@ -0,0 +1,28 @@ +parameters: + # Optional azure devops PAT with build execute permissions for the build's organization, + # only needed if the build that should be retained ran on a different organization than + # the pipeline where this template is executing from + Token: '' + # Optional BuildId to retain, defaults to the current running build + BuildId: '' + # Azure devops Organization URI for the build in the https://dev.azure.com/ format. + # Defaults to the organization the current pipeline is running on + AzdoOrgUri: '$(System.CollectionUri)' + # Azure devops project for the build. Defaults to the project the current pipeline is running on + AzdoProject: '$(System.TeamProject)' + +steps: + - task: powershell@2 + inputs: + targetType: 'filePath' + filePath: eng/common/retain-build.ps1 + pwsh: true + arguments: > + -AzdoOrgUri: ${{parameters.AzdoOrgUri}} + -AzdoProject ${{parameters.AzdoProject}} + -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} + -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} + displayName: Enable permanent build retention + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + BUILD_ID: $(Build.BuildId) \ No newline at end of file diff --git a/eng/common/core-templates/steps/send-to-helix.yml b/eng/common/core-templates/steps/send-to-helix.yml new file mode 100644 index 0000000000000..68fa739c4ab21 --- /dev/null +++ b/eng/common/core-templates/steps/send-to-helix.yml @@ -0,0 +1,93 @@ +# Please remember to update the documentation if you make changes to these parameters! +parameters: + HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ + HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' + HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number + HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues + HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixProjectPath: 'eng/common/helixpublish.proj' # optional -- path to the project file to build relative to BUILD_SOURCESDIRECTORY + HelixProjectArguments: '' # optional -- arguments passed to the build command + HelixConfiguration: '' # optional -- additional property attached to a job + HelixPreCommands: '' # optional -- commands to run before Helix work item execution + HelixPostCommands: '' # optional -- commands to run after Helix work item execution + WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects + WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects + WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects + CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload + XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true + XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects + XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects + XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner + XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects + IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion + DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." + IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set + HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) + Creator: '' # optional -- if the build is external, use this to specify who is sending the job + DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO + condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() + continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false + +steps: + - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' + displayName: ${{ parameters.DisplayNamePrefix }} (Windows) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog + displayName: ${{ parameters.DisplayNamePrefix }} (Unix) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml new file mode 100644 index 0000000000000..bdd725b496f91 --- /dev/null +++ b/eng/common/core-templates/steps/source-build.yml @@ -0,0 +1,134 @@ +parameters: + # This template adds arcade-powered source-build to CI. + + # This is a 'steps' template, and is intended for advanced scenarios where the existing build + # infra has a careful build methodology that must be followed. For example, a repo + # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline + # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to + # GitHub. Using this steps template leaves room for that infra to be included. + + # Defines the platform on which to run the steps. See 'eng/common/core-templates/job/source-build.yml' + # for details. The entire object is described in the 'job' template for simplicity, even though + # the usage of the properties on this object is split between the 'job' and 'steps' templates. + platform: {} + is1ESPipeline: false + +steps: +# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) +- script: | + set -x + df -h + + # If building on the internal project, the artifact feeds variable may be available (usually only if needed) + # In that case, call the feed setup script to add internal feeds corresponding to public ones. + # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. + # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those + # changes. + internalRestoreArgs= + if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then + # Temporarily work around https://github.com/dotnet/arcade/issues/7709 + chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh + $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) + internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' + + # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. + # This only works if there is a username/email configured, which won't be the case in most CI runs. + git config --get user.email + if [ $? -ne 0 ]; then + git config user.email dn-bot@microsoft.com + git config user.name dn-bot + fi + fi + + # If building on the internal project, the internal storage variable may be available (usually only if needed) + # In that case, add variables to allow the download of internal runtimes if the specified versions are not found + # in the default public locations. + internalRuntimeDownloadArgs= + if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' + fi + + buildConfig=Release + # Check if AzDO substitutes in a build config from a variable, and use it if so. + if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then + buildConfig='$(_BuildConfig)' + fi + + officialBuildArgs= + if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then + officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' + fi + + targetRidArgs= + if [ '${{ parameters.platform.targetRID }}' != '' ]; then + targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' + fi + + runtimeOsArgs= + if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then + runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' + fi + + baseOsArgs= + if [ '${{ parameters.platform.baseOS }}' != '' ]; then + baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' + fi + + publishArgs= + if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then + publishArgs='--publish' + fi + + assetManifestFileName=SourceBuild_RidSpecific.xml + if [ '${{ parameters.platform.name }}' != '' ]; then + assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml + fi + + ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ + --configuration $buildConfig \ + --restore --build --pack $publishArgs -bl \ + $officialBuildArgs \ + $internalRuntimeDownloadArgs \ + $internalRestoreArgs \ + $targetRidArgs \ + $runtimeOsArgs \ + $baseOsArgs \ + /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ + /p:ArcadeBuildFromSource=true \ + /p:DotNetBuildSourceOnly=true \ + /p:DotNetBuildRepo=true \ + /p:AssetManifestFileName=$assetManifestFileName + displayName: Build + +# Upload build logs for diagnosis. +- task: CopyFiles@2 + displayName: Prepare BuildLogs staging directory + inputs: + SourceFolder: '$(Build.SourcesDirectory)' + Contents: | + **/*.log + **/*.binlog + artifacts/sb/prebuilt-report/** + TargetFolder: '$(Build.StagingDirectory)/BuildLogs' + CleanTargetFolder: true + continueOnError: true + condition: succeededOrFailed() + +- template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish BuildLogs + targetPath: '$(Build.StagingDirectory)/BuildLogs' + artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) + continueOnError: true + condition: succeededOrFailed() + +# Manually inject component detection so that we can ignore the source build upstream cache, which contains +# a nupkg cache of input packages (a local feed). +# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' +# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets +- task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection (Exclude upstream cache) + inputs: + ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' diff --git a/eng/common/core-templates/variables/pool-providers.yml b/eng/common/core-templates/variables/pool-providers.yml new file mode 100644 index 0000000000000..41053d382a2e1 --- /dev/null +++ b/eng/common/core-templates/variables/pool-providers.yml @@ -0,0 +1,8 @@ +parameters: + is1ESPipeline: false + +variables: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/variables/pool-providers.yml + - ${{ else }}: + - template: /eng/common/templates/variables/pool-providers.yml \ No newline at end of file diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 9caf9b021dbdd..a8e35df7cee14 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -8,7 +8,7 @@ usage() echo "BuildArch can be: arm(default), arm64, armel, armv6, ppc64le, riscv64, s390x, x64, x86" echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine" echo " for alpine can be specified with version: alpineX.YY or alpineedge" - echo " for FreeBSD can be: freebsd12, freebsd13" + echo " for FreeBSD can be: freebsd13, freebsd14" echo " for illumos can be: illumos" echo " for Haiku can be: haiku." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" @@ -71,9 +71,9 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="12.4-RELEASE" +__FreeBSDBase="13.2-RELEASE" __FreeBSDPkg="1.17.0" -__FreeBSDABI="12" +__FreeBSDABI="13" __FreeBSDPackages="libunwind" __FreeBSDPackages+=" icu" __FreeBSDPackages+=" libinotify" @@ -142,7 +142,6 @@ while :; do case $lowerI in -\?|-h|--help) usage - exit 1 ;; arm) __BuildArch=arm @@ -182,12 +181,12 @@ while :; do __AlpinePackages="${__AlpinePackages// lldb-dev/}" __QEMUArch=riscv64 __UbuntuArch=riscv64 - __UbuntuRepo="http://deb.debian.org/debian-ports" + __UbuntuRepo="http://deb.debian.org/debian" __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" unset __LLDB_Package - if [[ -e "/usr/share/keyrings/debian-ports-archive-keyring.gpg" ]]; then - __Keyring="--keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring" + if [[ -e "/usr/share/keyrings/debian-archive-keyring.gpg" ]]; then + __Keyring="--keyring /usr/share/keyrings/debian-archive-keyring.gpg --include=debian-archive-keyring" fi ;; ppc64le) @@ -229,12 +228,19 @@ while :; do __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" ;; lldb*) - version="${lowerI/lldb/}" - parts=(${version//./ }) + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + majorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && minorVersion="${version#*.}" + if [ -z "$minorVersion" ]; then + minorVersion=0 + fi # for versions > 6.0, lldb has dropped the minor version - if [[ "${parts[0]}" -gt 6 ]]; then - version="${parts[0]}" + if [ "$majorVersion" -le 6 ]; then + version="$majorVersion.$minorVersion" + else + version="$majorVersion" fi __LLDB_Package="liblldb-${version}-dev" @@ -243,15 +249,19 @@ while :; do unset __LLDB_Package ;; llvm*) - version="${lowerI/llvm/}" - parts=(${version//./ }) - __LLVM_MajorVersion="${parts[0]}" - __LLVM_MinorVersion="${parts[1]}" - - # for versions > 6.0, llvm has dropped the minor version - if [[ -z "$__LLVM_MinorVersion" && "$__LLVM_MajorVersion" -le 6 ]]; then - __LLVM_MinorVersion=0; + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __LLVM_MajorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && __LLVM_MinorVersion="${version#*.}" + if [ -z "$__LLVM_MinorVersion" ]; then + __LLVM_MinorVersion=0 + fi + + # for versions > 6.0, lldb has dropped the minor version + if [ "$__LLVM_MajorVersion" -gt 6 ]; then + __LLVM_MinorVersion= fi + ;; xenial) # Ubuntu 16.04 if [[ "$__CodeName" != "jessie" ]]; then @@ -323,25 +333,24 @@ while :; do alpine*) __CodeName=alpine __UbuntuRepo= - version="${lowerI/alpine/}" - if [[ "$version" == "edge" ]]; then + if [[ "$lowerI" == "alpineedge" ]]; then __AlpineVersion=edge else - parts=(${version//./ }) - __AlpineMajorVersion="${parts[0]}" - __AlpineMinoVersion="${parts[1]}" - __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinoVersion" + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __AlpineMajorVersion="${version%%.*}" + __AlpineMinorVersion="${version#*.}" + __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinorVersion" fi ;; - freebsd12) + freebsd13) __CodeName=freebsd __SkipUnmount=1 ;; - freebsd13) + freebsd14) __CodeName=freebsd - __FreeBSDBase="13.2-RELEASE" - __FreeBSDABI="13" + __FreeBSDBase="14.0-RELEASE" + __FreeBSDABI="14" __SkipUnmount=1 ;; illumos) @@ -442,13 +451,39 @@ fi mkdir -p "$__RootfsDir" __RootfsDir="$( cd "$__RootfsDir" && pwd )" +__hasWget= +ensureDownloadTool() +{ + if command -v wget &> /dev/null; then + __hasWget=1 + elif command -v curl &> /dev/null; then + __hasWget=0 + else + >&2 echo "ERROR: either wget or curl is required by this script." + exit 1 + fi +} + if [[ "$__CodeName" == "alpine" ]]; then __ApkToolsVersion=2.12.11 - __ApkToolsSHA512SUM=53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33 __ApkToolsDir="$(mktemp -d)" __ApkKeysDir="$(mktemp -d)" + arch="$(uname -m)" - wget "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic//v$__ApkToolsVersion/x86_64/apk.static" -P "$__ApkToolsDir" + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + else + curl -SLO --create-dirs --output-dir "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + fi + if [[ "$arch" == "x86_64" ]]; then + __ApkToolsSHA512SUM="53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33" + elif [[ "$arch" == "aarch64" ]]; then + __ApkToolsSHA512SUM="9e2b37ecb2b56c05dad23d379be84fd494c14bd730b620d0d576bda760588e1f2f59a7fcb2f2080577e0085f23a0ca8eadd993b4e61c2ab29549fdb71969afd0" + else + echo "WARNING: add missing hash for your host architecture. To find the value, use: 'find /tmp -name apk.static -exec sha512sum {} \;'" + fi echo "$__ApkToolsSHA512SUM $__ApkToolsDir/apk.static" | sha512sum -c chmod +x "$__ApkToolsDir/apk.static" @@ -477,20 +512,23 @@ if [[ "$__CodeName" == "alpine" ]]; then fi # initialize DB + # shellcheck disable=SC2086 "$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" --initdb add if [[ "$__AlpineLlvmLibsLookup" == 1 ]]; then + # shellcheck disable=SC2086 __AlpinePackages+=" $("$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \ - search 'llvm*-libs' | sort | tail -1 | sed 's/-[^-]*//2g')" + search 'llvm*-libs' | grep -E '^llvm' | sort | tail -1 | sed 's/-[^-]*//2g')" fi # install all packages in one go + # shellcheck disable=SC2086 "$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ @@ -501,12 +539,23 @@ if [[ "$__CodeName" == "alpine" ]]; then elif [[ "$__CodeName" == "freebsd" ]]; then mkdir -p "$__RootfsDir"/usr/local/etc JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} - wget -O - "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + else + curl -SL "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + fi echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"${__RootfsDir}/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf mkdir -p "$__RootfsDir"/tmp # get and build package manager - wget -O - "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + else + curl -SL "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + fi cd "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" # needed for install to succeed mkdir -p "$__RootfsDir"/host/etc @@ -514,20 +563,36 @@ elif [[ "$__CodeName" == "freebsd" ]]; then rm -rf "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" # install packages we need. INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update + # shellcheck disable=SC2086 INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf install --yes $__FreeBSDPackages elif [[ "$__CodeName" == "illumos" ]]; then mkdir "$__RootfsDir/tmp" pushd "$__RootfsDir/tmp" JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + ensureDownloadTool + echo "Downloading sysroot." - wget -O - https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + else + curl -SL https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + fi echo "Building binutils. Please wait.." - wget -O - https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + else + curl -SL https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + fi mkdir build-binutils && cd build-binutils ../binutils-2.33.1/configure --prefix="$__RootfsDir" --target="${__illumosArch}-sun-solaris2.10" --program-prefix="${__illumosArch}-illumos-" --with-sysroot="$__RootfsDir" make -j "$JOBS" && make install && cd .. echo "Building gcc. Please wait.." - wget -O - https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + else + curl -SL https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + fi CFLAGS="-fPIC" CXXFLAGS="-fPIC" CXXFLAGS_FOR_TARGET="-fPIC" @@ -544,7 +609,11 @@ elif [[ "$__CodeName" == "illumos" ]]; then fi BaseUrl="$BaseUrl/packages/SmartOS/trunk/${__illumosArch}/All" echo "Downloading manifest" - wget "$BaseUrl" + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl" + else + curl -SLO "$BaseUrl" + fi echo "Downloading dependencies." read -ra array <<<"$__IllumosPackages" for package in "${array[@]}"; do @@ -552,7 +621,11 @@ elif [[ "$__CodeName" == "illumos" ]]; then # find last occurrence of package in listing and extract its name package="$(sed -En '/.*href="('"$package"'-[0-9].*).tgz".*/h;$!d;g;s//\1/p' All)" echo "Resolved name '$package'" - wget "$BaseUrl"/"$package".tgz + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl"/"$package".tgz + else + curl -SLO "$BaseUrl"/"$package".tgz + fi ar -x "$package".tgz tar --skip-old-files -xzf "$package".tmp.tg* -C "$__RootfsDir" 2>/dev/null done @@ -561,10 +634,17 @@ elif [[ "$__CodeName" == "illumos" ]]; then rm -rf "$__RootfsDir"/{tmp,+*} mkdir -p "$__RootfsDir"/usr/include/net mkdir -p "$__RootfsDir"/usr/include/netpacket - wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h - wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h - wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h - wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + else + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + fi elif [[ "$__CodeName" == "haiku" ]]; then JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} @@ -574,9 +654,16 @@ elif [[ "$__CodeName" == "haiku" ]]; then mkdir "$__RootfsDir/tmp/download" + ensureDownloadTool + echo "Downloading Haiku package tool" - git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 $__RootfsDir/tmp/script - wget -O "$__RootfsDir/tmp/download/hosttools.zip" $($__RootfsDir/tmp/script/fetch.sh --hosttools) + git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 "$__RootfsDir/tmp/script" + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + else + curl -SLo "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + fi + unzip -o "$__RootfsDir/tmp/download/hosttools.zip" -d "$__RootfsDir/tmp/bin" DepotBaseUrl="https://depot.haiku-os.org/__api/v2/pkg/get-pkg" @@ -589,14 +676,25 @@ elif [[ "$__CodeName" == "haiku" ]]; then echo "Downloading $package..." # API documented here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L60 # The schema here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L598 - hpkgDownloadUrl="$(wget -qO- --post-data='{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ - --header='Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" - wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + if [[ "$__hasWget" == 1 ]]; then + hpkgDownloadUrl="$(wget -qO- --post-data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + else + hpkgDownloadUrl="$(curl -sSL -XPOST --data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + fi done for package in haiku haiku_devel; do echo "Downloading $package..." - hpkgVersion="$(wget -qO- $HpkgBaseUrl | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" - wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + if [[ "$__hasWget" == 1 ]]; then + hpkgVersion="$(wget -qO- "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + else + hpkgVersion="$(curl -sSL "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + fi done # Set up the sysroot @@ -609,7 +707,11 @@ elif [[ "$__CodeName" == "haiku" ]]; then # Download buildtools echo "Downloading Haiku buildtools" - wget -O "$__RootfsDir/tmp/download/buildtools.zip" $($__RootfsDir/tmp/script/fetch.sh --buildtools --arch=$__HaikuArch) + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + else + curl -SLo "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + fi unzip -o "$__RootfsDir/tmp/download/buildtools.zip" -d "$__RootfsDir" # Cleaning up temporary files @@ -622,10 +724,12 @@ elif [[ -n "$__CodeName" ]]; then __Keyring="$__Keyring --force-check-gpg" fi + # shellcheck disable=SC2086 debootstrap "--variant=minbase" $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo" cp "$__CrossDir/$__BuildArch/sources.list.$__CodeName" "$__RootfsDir/etc/apt/sources.list" chroot "$__RootfsDir" apt-get update chroot "$__RootfsDir" apt-get -f -y install + # shellcheck disable=SC2086 chroot "$__RootfsDir" apt-get -y install $__UbuntuPackages chroot "$__RootfsDir" symlinks -cr /usr chroot "$__RootfsDir" apt-get clean @@ -643,6 +747,5 @@ elif [[ "$__Tizen" == "tizen" ]]; then ROOTFS_DIR="$__RootfsDir" "$__CrossDir/tizen-build-rootfs.sh" "$__BuildArch" else echo "Unsupported target platform." - usage; - exit 1 + usage fi diff --git a/eng/common/cross/riscv64/sources.list.sid b/eng/common/cross/riscv64/sources.list.sid index 65f730d224caa..b5f7a7e6e1eb5 100644 --- a/eng/common/cross/riscv64/sources.list.sid +++ b/eng/common/cross/riscv64/sources.list.sid @@ -1 +1 @@ -deb http://deb.debian.org/debian-ports sid main +deb http://deb.debian.org/debian sid main diff --git a/eng/common/cross/riscv64/tizen/tizen.patch b/eng/common/cross/riscv64/tizen/tizen.patch new file mode 100644 index 0000000000000..eb6d1c07470bf --- /dev/null +++ b/eng/common/cross/riscv64/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf64-littleriscv) +-GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-riscv64-lp64d.so.1 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-riscv64-lp64d.so.1 ) ) diff --git a/eng/common/cross/tizen-build-rootfs.sh b/eng/common/cross/tizen-build-rootfs.sh index ac84173d44fc3..ba31c93285f63 100644 --- a/eng/common/cross/tizen-build-rootfs.sh +++ b/eng/common/cross/tizen-build-rootfs.sh @@ -22,6 +22,10 @@ case "$ARCH" in TIZEN_ARCH="x86_64" LINK_ARCH="x86" ;; + riscv64) + TIZEN_ARCH="riscv64" + LINK_ARCH="riscv" + ;; *) echo "Unsupported architecture for tizen: $ARCH" exit 1 @@ -58,4 +62,21 @@ rm -rf $TIZEN_TMP_DIR echo ">>Start configuring Tizen rootfs" ln -sfn asm-${LINK_ARCH} ./usr/include/asm patch -p1 < $__TIZEN_CROSSDIR/tizen.patch +if [[ "$TIZEN_ARCH" == "riscv64" ]]; then + echo "Fixing broken symlinks in $PWD" + rm ./usr/lib64/libresolv.so + ln -s ../../lib64/libresolv.so.2 ./usr/lib64/libresolv.so + rm ./usr/lib64/libpthread.so + ln -s ../../lib64/libpthread.so.0 ./usr/lib64/libpthread.so + rm ./usr/lib64/libdl.so + ln -s ../../lib64/libdl.so.2 ./usr/lib64/libdl.so + rm ./usr/lib64/libutil.so + ln -s ../../lib64/libutil.so.1 ./usr/lib64/libutil.so + rm ./usr/lib64/libm.so + ln -s ../../lib64/libm.so.6 ./usr/lib64/libm.so + rm ./usr/lib64/librt.so + ln -s ../../lib64/librt.so.1 ./usr/lib64/librt.so + rm ./lib/ld-linux-riscv64-lp64d.so.1 + ln -s ../lib64/ld-linux-riscv64-lp64d.so.1 ./lib/ld-linux-riscv64-lp64d.so.1 +fi echo "<:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) + add_link_options($<$:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) +endif() + +option(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC "Statically link against the C++ standard library" OFF) +if(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC) + add_link_options($<$:-static-libstdc++>) +endif() + +set(CLR_CMAKE_CXX_ABI_LIBRARY "" CACHE STRING "C++ ABI implementation library to link against. Only supported with the Clang compiler.") +if (CLR_CMAKE_CXX_ABI_LIBRARY) + # The user may specify the ABI library with the 'lib' prefix, like 'libstdc++'. Strip the prefix here so the linker finds the right library. + string(REGEX REPLACE "^lib(.+)" "\\1" CLR_CMAKE_CXX_ABI_LIBRARY ${CLR_CMAKE_CXX_ABI_LIBRARY}) + # We need to specify this as a linker-backend option as Clang will filter this option out when linking to libc++. + add_link_options("LINKER:-l${CLR_CMAKE_CXX_ABI_LIBRARY}") +endif() + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/eng/common/helixpublish.proj b/eng/common/helixpublish.proj index d7f185856e791..c1323bf412105 100644 --- a/eng/common/helixpublish.proj +++ b/eng/common/helixpublish.proj @@ -1,3 +1,4 @@ + diff --git a/eng/common/internal/Directory.Build.props b/eng/common/internal/Directory.Build.props index dbf99d82a5c2e..a735fe9a133ca 100644 --- a/eng/common/internal/Directory.Build.props +++ b/eng/common/internal/Directory.Build.props @@ -1,4 +1,6 @@ + + diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index 7f5ce6d608133..8fa77e5b181f8 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -1,5 +1,6 @@ + net472 false @@ -27,4 +28,5 @@ + diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index 2d5660642b8d4..ccd3a17268e24 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -64,7 +64,7 @@ if [ -z "$CLR_CC" ]; then if [ -z "$majorVersion" ]; then # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. if [ "$compiler" = "clang" ]; then versions="18 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5" - elif [ "$compiler" = "gcc" ]; then versions="13 12 11 10 9 8 7 6 5 4.9"; fi + elif [ "$compiler" = "gcc" ]; then versions="14 13 12 11 10 9 8 7 6 5 4.9"; fi for version in $versions; do _major="${version%%.*}" @@ -125,8 +125,8 @@ if [ -z "$CC" ]; then exit 1 fi -# Only lld version >= 9 can be considered stable. lld doesn't support s390x. -if [ "$compiler" = "clang" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -ge 9 ] && [ "$build_arch" != "s390x" ]; then +# Only lld version >= 9 can be considered stable. lld supports s390x starting from 18.0. +if [ "$compiler" = "clang" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -ge 9 ] && ([ "$build_arch" != "s390x" ] || [ "$majorVersion" -ge 18 ]); then if "$CC" -fuse-ld=lld -Wl,--version >/dev/null 2>&1; then LDFLAGS="-fuse-ld=lld" fi diff --git a/eng/common/native/init-distro-rid.sh b/eng/common/native/init-distro-rid.sh index de1687b2ccbe7..83ea7aab0e081 100644 --- a/eng/common/native/init-distro-rid.sh +++ b/eng/common/native/init-distro-rid.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # getNonPortableDistroRid # @@ -11,21 +11,16 @@ # non-portable rid getNonPortableDistroRid() { - local targetOs="$1" - local targetArch="$2" - local rootfsDir="$3" - local nonPortableRid="" + targetOs="$1" + targetArch="$2" + rootfsDir="$3" + nonPortableRid="" if [ "$targetOs" = "linux" ]; then + # shellcheck disable=SC1091 if [ -e "${rootfsDir}/etc/os-release" ]; then - source "${rootfsDir}/etc/os-release" - - if [[ "${ID}" == "rhel" || "${ID}" == "rocky" || "${ID}" == "alpine" ]]; then - # remove the last version digit - VERSION_ID="${VERSION_ID%.*}" - fi - - if [[ "${VERSION_ID:-}" =~ ^([[:digit:]]|\.)+$ ]]; then + . "${rootfsDir}/etc/os-release" + if echo "${VERSION_ID:-}" | grep -qE '^([[:digit:]]|\.)+$'; then nonPortableRid="${ID}.${VERSION_ID}-${targetArch}" else # Rolling release distros either do not set VERSION_ID, set it as blank or @@ -33,45 +28,33 @@ getNonPortableDistroRid() # so omit it here to be consistent with everything else. nonPortableRid="${ID}-${targetArch}" fi - elif [ -e "${rootfsDir}/android_platform" ]; then - source "$rootfsDir"/android_platform + # shellcheck disable=SC1091 + . "${rootfsDir}/android_platform" nonPortableRid="$RID" fi fi if [ "$targetOs" = "freebsd" ]; then - # $rootfsDir can be empty. freebsd-version is shell script and it should always work. - __freebsd_major_version=$($rootfsDir/bin/freebsd-version | { read v; echo "${v%%.*}"; }) + # $rootfsDir can be empty. freebsd-version is a shell script and should always work. + __freebsd_major_version=$("$rootfsDir"/bin/freebsd-version | cut -d'.' -f1) nonPortableRid="freebsd.$__freebsd_major_version-${targetArch}" - elif command -v getprop && getprop ro.product.system.model 2>&1 | grep -qi android; then + elif command -v getprop >/dev/null && getprop ro.product.system.model | grep -qi android; then __android_sdk_version=$(getprop ro.build.version.sdk) nonPortableRid="android.$__android_sdk_version-${targetArch}" elif [ "$targetOs" = "illumos" ]; then __uname_version=$(uname -v) - case "$__uname_version" in - omnios-*) - __omnios_major_version=$(echo "${__uname_version:8:2}") - nonPortableRid=omnios."$__omnios_major_version"-"$targetArch" - ;; - joyent_*) - __smartos_major_version=$(echo "${__uname_version:7:4}") - nonPortableRid=smartos."$__smartos_major_version"-"$targetArch" - ;; - illumos_*) - nonPortableRid=openindiana-"$targetArch" - ;; - esac + nonPortableRid="illumos-${targetArch}" elif [ "$targetOs" = "solaris" ]; then __uname_version=$(uname -v) - __solaris_major_version=$(echo "${__uname_version%.*}") - nonPortableRid=solaris."$__solaris_major_version"-"$targetArch" + __solaris_major_version=$(echo "$__uname_version" | cut -d'.' -f1) + nonPortableRid="solaris.$__solaris_major_version-${targetArch}" elif [ "$targetOs" = "haiku" ]; then - __uname_release=$(uname -r) + __uname_release="$(uname -r)" nonPortableRid=haiku.r"$__uname_release"-"$targetArch" fi - echo "$(echo $nonPortableRid | tr '[:upper:]' '[:lower:]')" + echo "$nonPortableRid" | tr '[:upper:]' '[:lower:]' } # initDistroRidGlobal @@ -85,26 +68,23 @@ getNonPortableDistroRid() # None # # Notes: -# -# It is important to note that the function does not return anything, but it -# exports the following variables on success: -# -# __DistroRid : Non-portable rid of the target platform. -# __PortableTargetOS : OS-part of the portable rid that corresponds to the target platform. -# +# It is important to note that the function does not return anything, but it +# exports the following variables on success: +# __DistroRid : Non-portable rid of the target platform. +# __PortableTargetOS : OS-part of the portable rid that corresponds to the target platform. initDistroRidGlobal() { - local targetOs="$1" - local targetArch="$2" - local rootfsDir="" - if [ "$#" -ge 3 ]; then + targetOs="$1" + targetArch="$2" + rootfsDir="" + if [ $# -ge 3 ]; then rootfsDir="$3" fi if [ -n "${rootfsDir}" ]; then # We may have a cross build. Check for the existence of the rootfsDir if [ ! -e "${rootfsDir}" ]; then - echo "Error rootfsDir has been passed, but the location is not valid." + echo "Error: rootfsDir has been passed, but the location is not valid." exit 1 fi fi @@ -119,7 +99,7 @@ initDistroRidGlobal() STRINGS="$(command -v llvm-strings || true)" fi - # Check for musl-based distros (e.g Alpine Linux, Void Linux). + # Check for musl-based distros (e.g. Alpine Linux, Void Linux). if "${rootfsDir}/usr/bin/ldd" --version 2>&1 | grep -q musl || ( [ -n "$STRINGS" ] && "$STRINGS" "${rootfsDir}/usr/bin/ldd" 2>&1 | grep -q musl ); then __PortableTargetOS="linux-musl" diff --git a/eng/common/native/init-os-and-arch.sh b/eng/common/native/init-os-and-arch.sh index e693617a6c2b6..38921d4338f74 100644 --- a/eng/common/native/init-os-and-arch.sh +++ b/eng/common/native/init-os-and-arch.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Use uname to determine what the OS is. OSName=$(uname -s | tr '[:upper:]' '[:lower:]') @@ -35,6 +35,10 @@ fi case "$CPUName" in arm64|aarch64) arch=arm64 + if [ "$(getconf LONG_BIT)" -lt 64 ]; then + # This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS) + arch=arm + fi ;; loongarch64) @@ -50,6 +54,7 @@ case "$CPUName" in ;; armv7l|armv8l) + # shellcheck disable=SC1091 if (NAME=""; . /etc/os-release; test "$NAME" = "Tizen"); then arch=armel else diff --git a/eng/common/post-build/check-channel-consistency.ps1 b/eng/common/post-build/check-channel-consistency.ps1 index 63f3464c986a7..1728f035a93ed 100644 --- a/eng/common/post-build/check-channel-consistency.ps1 +++ b/eng/common/post-build/check-channel-consistency.ps1 @@ -7,7 +7,7 @@ try { . $PSScriptRoot\post-build-utils.ps1 if ($PromoteToChannels -eq "") { - Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." + Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." ExitWithExitCode 0 } diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 new file mode 100644 index 0000000000000..82d91f6fd0226 --- /dev/null +++ b/eng/common/post-build/redact-logs.ps1 @@ -0,0 +1,81 @@ +[CmdletBinding(PositionalBinding=$False)] +param( + [Parameter(Mandatory=$true, Position=0)][string] $InputPath, + [Parameter(Mandatory=$true)][string] $BinlogToolVersion, + [Parameter(Mandatory=$false)][string] $DotnetPath, + [Parameter(Mandatory=$false)][string] $PackageFeed = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json', + # File with strings to redact - separated by newlines. + # For comments start the line with '# ' - such lines are ignored + [Parameter(Mandatory=$false)][string] $TokensFilePath, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $packageName = 'binlogtool' + + $dotnet = $DotnetPath + + if (!$dotnet) { + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + } + + $toolList = & "$dotnet" tool list -g + + if ($toolList -like "*$packageName*") { + & "$dotnet" tool uninstall $packageName -g + } + + $toolPath = "$PSScriptRoot\..\..\..\.tools" + $verbosity = 'minimal' + + New-Item -ItemType Directory -Force -Path $toolPath + + Push-Location -Path $toolPath + + try { + Write-Host "Installing Binlog redactor CLI..." + Write-Host "'$dotnet' new tool-manifest" + & "$dotnet" new tool-manifest + Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" + & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion + + if (Test-Path $TokensFilePath) { + Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath + $TokensToRedact += Get-Content -Path $TokensFilePath | Foreach {$_.Trim()} | Where { $_ -notmatch "^# " } + } + + $optionalParams = [System.Collections.ArrayList]::new() + + Foreach ($p in $TokensToRedact) + { + if($p -match '^\$\(.*\)$') + { + Write-Host ("Ignoring token {0} as it is probably unexpanded AzDO variable" -f $p) + } + elseif($p) + { + $optionalParams.Add("-p:" + $p) | Out-Null + } + } + + & $dotnet binlogtool redact --input:$InputPath --recurse --in-place ` + @optionalParams + + if ($LastExitCode -ne 0) { + Write-PipelineTelemetryError -Category 'Redactor' -Type 'warning' -Message "Problems using Redactor tool (exit code: $LastExitCode). But ignoring them now." + } + } + finally { + Pop-Location + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'Redactor' -Message "There was an error while trying to redact logs. Error: $_" + ExitWithExitCode 1 +} diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 73828dd30d317..aab40de3fd9ac 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.8.1-2" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.10.0-pre.4.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/sdl/trim-assets-version.ps1 b/eng/common/sdl/trim-assets-version.ps1 index a2e0048770452..0daa2a9e94628 100644 --- a/eng/common/sdl/trim-assets-version.ps1 +++ b/eng/common/sdl/trim-assets-version.ps1 @@ -72,4 +72,4 @@ catch { Write-Host $_ Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ ExitWithExitCode 1 -} \ No newline at end of file +} diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md new file mode 100644 index 0000000000000..c114bc28dcb95 --- /dev/null +++ b/eng/common/template-guidance.md @@ -0,0 +1,137 @@ +# Overview + +Arcade provides templates for public (`/templates`) and 1ES pipeline templates (`/templates-official`) scenarios. Pipelines which are required to be managed by 1ES pipeline templates should reference `/templates-offical`, all other pipelines may reference `/templates`. + +## How to use + +Basic guidance is: + +- 1ES Pipeline Template or 1ES Microbuild template runs should reference `eng/common/templates-official`. Any internal production-graded pipeline should use these templates. + +- All other runs should reference `eng/common/templates`. + +See [azure-pipelines.yml](../../azure-pipelines.yml) (templates-official example) or [azure-pipelines-pr.yml](../../azure-pipelines-pr.yml) (templates example) for examples. + +#### The `templateIs1ESManaged` parameter + +The `templateIs1ESManaged` is available on most templates and affects which of the variants is used for nested templates. See [Development Notes](#development-notes) below for more information on the `templateIs1ESManaged1 parameter. + +- For templates under `job/`, `jobs/`, `steps`, or `post-build/`, this parameter must be explicitly set. + +## Multiple outputs + +1ES pipeline templates impose a policy where every publish artifact execution results in additional security scans being injected into your pipeline. When using `templates-official/jobs/jobs.yml`, Arcade reduces the number of additional security injections by gathering all publishing outputs into the [Build.ArtifactStagingDirectory](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services), and utilizing the [outputParentDirectory](https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/outputs#multiple-outputs) feature of 1ES pipeline templates. When implementing your pipeline, if you ensure publish artifacts are located in the `$(Build.ArtifactStagingDirectory)`, and utilize the 1ES provided template context, then you can reduce the number of security scans for your pipeline. + +Example: +``` yaml +# azure-pipelines.yml +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + stages: + - stage: build + jobs: + - template: /eng/common/templates-official/jobs/jobs.yml@self + parameters: + # 1ES makes use of outputs to reduce security task injection overhead + templateContext: + outputs: + - output: pipelineArtifact + displayName: 'Publish logs from source' + continueOnError: true + condition: always() + targetPath: $(Build.ArtifactStagingDirectory)/artifacts/log + artifactName: Logs + jobs: + - job: Windows + steps: + - script: echo "friendly neighborhood" > artifacts/marvel/spiderman.txt + # copy build outputs to artifact staging directory for publishing + - task: CopyFiles@2 + displayName: Gather build output + inputs: + SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' +``` + +Note: Multiple outputs are ONLY applicable to 1ES PT publishing (only usable when referencing `templates-official`). + +# Development notes + +**Folder / file structure** + +``` text +eng\common\ + [templates || templates-official]\ + job\ + job.yml (shim + artifact publishing logic) + onelocbuild.yml (shim) + publish-build-assets.yml (shim) + source-build.yml (shim) + source-index-stage1.yml (shim) + jobs\ + codeql-build.yml (shim) + jobs.yml (shim) + source-build.yml (shim) + post-build\ + post-build.yml (shim) + trigger-subscription.yml (shim) + common-variabls.yml (shim) + setup-maestro-vars.yml (shim) + steps\ + publish-build-artifacts.yml (logic) + publish-pipeline-artifacts.yml (logic) + add-build-channel.yml (shim) + component-governance.yml (shim) + generate-sbom.yml (shim) + publish-logs.yml (shim) + retain-build.yml (shim) + send-to-helix.yml (shim) + source-build.yml (shim) + variables\ + pool-providers.yml (logic + redirect) # templates/variables/pool-providers.yml will redirect to templates-official/variables/pool-providers.yml if you are running in the internal project + sdl-variables.yml (logic) + core-templates\ + job\ + job.yml (logic) + onelocbuild.yml (logic) + publish-build-assets.yml (logic) + source-build.yml (logic) + source-index-stage1.yml (logic) + jobs\ + codeql-build.yml (logic) + jobs.yml (logic) + source-build.yml (logic) + post-build\ + common-variabls.yml (logic) + post-build.yml (logic) + setup-maestro-vars.yml (logic) + trigger-subscription.yml (logic) + steps\ + add-build-to-channel.yml (logic) + component-governance.yml (logic) + generate-sbom.yml (logic) + publish-build-artifacts.yml (redirect) + publish-logs.yml (logic) + publish-pipeline-artifacts.yml (redirect) + retain-build.yml (logic) + send-to-helix.yml (logic) + source-build.yml (logic) + variables\ + pool-providers.yml (redirect) +``` + +In the table above, a file is designated as "shim", "logic", or "redirect". + +- shim - represents a yaml file which is an intermediate step between pipeline logic and .Net Core Engineering's templates (`core-templates`) and defines the `is1ESPipeline` parameter value. + +- logic - represents actual base template logic. + +- redirect- represents a file in `core-templates` which redirects to the "logic" file in either `templates` or `templates-official`. + +Logic for Arcade's templates live **primarily** in the `core-templates` folder. The exceptions to the location of the logic files are around artifact publishing, which is handled differently between 1es pipeline templates and standard templates. `templates` and `templates-official` provide shim entry points which redirect to `core-templates` while also defining the `is1ESPipeline` parameter. If a shim is referenced in `templates`, then `is1ESPipeline` is set to `false`. If a shim is referenced in `templates-official`, then `is1ESPipeline` is set to `true`. + +Within `templates` and `templates-official`, the templates at the "stages", and "jobs" / "job" level have been replaced with shims. Templates at the "steps" and "variables" level are typically too granular to be replaced with shims and instead persist logic which is directly applicable to either scenario. + +Within `core-templates`, there are a handful of places where logic is dependent on which shim entry point was used. In those places, we redirect back to the respective logic file in `templates` or `templates-official`. diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 1f035fee73f4a..4724e9aaa8091 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -1,264 +1,62 @@ -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - -parameters: -# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - cancelTimeoutInMinutes: '' - condition: '' - container: '' - continueOnError: false - dependsOn: '' - displayName: '' - pool: '' - steps: [] - strategy: '' - timeoutInMinutes: '' - variables: [] - workspace: '' - templateContext: '' - -# Job base template specific parameters - # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md - artifacts: '' - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishBuildAssets: false - enablePublishTestResults: false - enablePublishUsingPipelines: false - enableBuildRetry: false - disableComponentGovernance: '' - componentGovernanceIgnoreDirectories: '' - mergeTestResults: false - testRunTitle: '' - testResultsFormat: '' - name: '' - preSteps: [] - runAsPublic: false -# Sbom related params - enableSbom: true - PackageVersion: 7.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - jobs: -- job: ${{ parameters.name }} - - ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: - cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} - - ${{ if ne(parameters.condition, '') }}: - condition: ${{ parameters.condition }} - - ${{ if ne(parameters.container, '') }}: - container: ${{ parameters.container }} - - ${{ if ne(parameters.continueOnError, '') }}: - continueOnError: ${{ parameters.continueOnError }} - - ${{ if ne(parameters.dependsOn, '') }}: - dependsOn: ${{ parameters.dependsOn }} - - ${{ if ne(parameters.displayName, '') }}: - displayName: ${{ parameters.displayName }} - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - - ${{ if ne(parameters.strategy, '') }}: - strategy: ${{ parameters.strategy }} - - ${{ if ne(parameters.timeoutInMinutes, '') }}: - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - ${{ if ne(parameters.templateContext, '') }}: - templateContext: ${{ parameters.templateContext }} - - variables: - - ${{ if ne(parameters.enableTelemetry, 'false') }}: - - name: DOTNET_CLI_TELEMETRY_PROFILE - value: '$(Build.Repository.Uri)' - - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: - - name: EnableRichCodeNavigation - value: 'true' - # Retry signature validation up to three times, waiting 2 seconds between attempts. - # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures - - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY - value: 3,2000 - - ${{ each variable in parameters.variables }}: - # handle name-value variable syntax - # example: - # - name: [key] - # value: [value] - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - # handle variable groups - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - # handle template variable syntax - # example: - # - template: path/to/template.yml - # parameters: - # [key]: [value] - - ${{ if ne(variable.template, '') }}: - - template: ${{ variable.template }} - ${{ if ne(variable.parameters, '') }}: - parameters: ${{ variable.parameters }} - - # handle key-value variable syntax. - # example: - # - [key]: [value] - - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: - - ${{ each pair in variable }}: - - name: ${{ pair.key }} - value: ${{ pair.value }} - - # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds - - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: DotNet-HelixApi-Access - - ${{ if ne(parameters.workspace, '') }}: - workspace: ${{ parameters.workspace }} - - steps: - - ${{ if ne(parameters.preSteps, '') }}: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - - - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@1 - - - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: current - artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} - targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} - itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} - - - ${{ each step in parameters.steps }}: - - ${{ step }} - - - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} - environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }} - richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin - uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} - continueOnError: true - - - template: /eng/common/templates-official/steps/component-governance.yml - parameters: - ${{ if eq(parameters.disableComponentGovernance, '') }}: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: - disableComponentGovernance: false - ${{ else }}: - disableComponentGovernance: true - ${{ else }}: - disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks - condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - env: - TeamName: $(_TeamName) - - - ${{ if ne(parameters.artifacts.publish, '') }}: - - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - task: CopyFiles@2 - displayName: Gather binaries for publish to artifacts - inputs: - SourceFolder: 'artifacts/bin' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' - - task: CopyFiles@2 - displayName: Gather packages for publish to artifacts - inputs: - SourceFolder: 'artifacts/packages' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish pipeline artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} - continueOnError: true - condition: always() - - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: 'artifacts/log' - artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} - displayName: 'Publish logs' - continueOnError: true - condition: always() - - - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} - continueOnError: true - condition: always() - - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: - - task: PublishTestResults@2 - displayName: Publish XUnit Test Results - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: - - task: PublishTestResults@2 - displayName: Publish TRX Test Results - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - template: /eng/common/templates-official/steps/generate-sbom.yml - parameters: - PackageVersion: ${{ parameters.packageVersion}} - BuildDropPath: ${{ parameters.buildDropPath }} - IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' - artifactName: 'BuildConfiguration' - displayName: 'Publish build retry configuration' - continueOnError: true \ No newline at end of file +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: true + + # publish artifacts + # for 1ES managed templates, use the templateContext.output to handle multiple outputs. + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - output: buildArtifacts + displayName: Publish pipeline artifacts + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + condition: always() + continueOnError: true + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)_Attempt$(System.JobAttempt)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: + - output: buildArtifacts + displayName: Publish Logs + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/eng/common/BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - output: pipelineArtifact + displayName: Publish SBOM manifest + continueOnError: true + targetPath: $(Build.ArtifactStagingDirectory)/sbom + artifactName: $(ARTIFACT_NAME) + + # add any outputs provided via root yaml + - ${{ if ne(parameters.templateContext.outputs, '') }}: + - ${{ each output in parameters.templateContext.outputs }}: + - ${{ output }} + + # add any remaining templateContext properties + ${{ each context in parameters.templateContext }}: + ${{ if and(ne(context.key, 'outputParentDirectory'), ne(context.key, 'outputs')) }}: + ${{ context.key }}: ${{ context.value }} + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'templateContext'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/onelocbuild.yml b/eng/common/templates-official/job/onelocbuild.yml index 52b4d05d3f8dd..0f0c514b912df 100644 --- a/eng/common/templates-official/job/onelocbuild.yml +++ b/eng/common/templates-official/job/onelocbuild.yml @@ -1,112 +1,7 @@ -parameters: - # Optional: dependencies of the job - dependsOn: '' - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: '' - - CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex - GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - - SourcesDirectory: $(Build.SourcesDirectory) - CreatePr: true - AutoCompletePr: false - ReusePr: true - UseLfLineEndings: true - UseCheckedInLocProjectJson: false - SkipLocProjectJsonGeneration: false - LanguageSet: VS_Main_Languages - LclSource: lclFilesInRepo - LclPackageId: '' - RepoType: gitHub - GitHubOrg: dotnet - MirrorRepo: '' - MirrorBranch: main - condition: '' - JobNameSuffix: '' - jobs: -- job: OneLocBuild${{ parameters.JobNameSuffix }} - - dependsOn: ${{ parameters.dependsOn }} - - displayName: OneLocBuild${{ parameters.JobNameSuffix }} - - variables: - - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat - - name: _GenerateLocProjectArguments - value: -SourcesDirectory ${{ parameters.SourcesDirectory }} - -LanguageSet "${{ parameters.LanguageSet }}" - -CreateNeutralXlfs - - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: - - name: _GenerateLocProjectArguments - value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - - steps: - - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - - task: Powershell@2 - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 - arguments: $(_GenerateLocProjectArguments) - displayName: Generate LocProject.json - condition: ${{ parameters.condition }} - - - task: OneLocBuild@2 - displayName: OneLocBuild - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - locProj: eng/Localize/LocProject.json - outDir: $(Build.ArtifactStagingDirectory) - lclSource: ${{ parameters.LclSource }} - lclPackageId: ${{ parameters.LclPackageId }} - isCreatePrSelected: ${{ parameters.CreatePr }} - isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} - ${{ if eq(parameters.CreatePr, true) }}: - isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - isShouldReusePrSelected: ${{ parameters.ReusePr }} - packageSourceAuth: patAuth - patVariable: ${{ parameters.CeapexPat }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - repoType: ${{ parameters.RepoType }} - gitHubPatVariable: "${{ parameters.GithubPat }}" - ${{ if ne(parameters.MirrorRepo, '') }}: - isMirrorRepoSelected: true - gitHubOrganization: ${{ parameters.GitHubOrg }} - mirrorRepo: ${{ parameters.MirrorRepo }} - mirrorBranch: ${{ parameters.MirrorBranch }} - condition: ${{ parameters.condition }} - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Localization Files - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: true - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish LocProject.json - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml index 589ac80a18b74..d667a70e8de74 100644 --- a/eng/common/templates-official/job/publish-build-assets.yml +++ b/eng/common/templates-official/job/publish-build-assets.yml @@ -1,155 +1,7 @@ -parameters: - configuration: 'Debug' - - # Optional: condition for the job to run - condition: '' - - # Optional: 'true' if future jobs should run even if this job fails - continueOnError: false - - # Optional: dependencies of the job - dependsOn: '' - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: {} - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishUsingPipelines: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishAssetsImmediately: false - - artifactsPublishingAdditionalParameters: '' - - signingValidationAdditionalParameters: '' - jobs: -- job: Asset_Registry_Publish - - dependsOn: ${{ parameters.dependsOn }} - timeoutInMinutes: 150 - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - displayName: Publish Assets - ${{ else }}: - displayName: Publish to Build Asset Registry - - variables: - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: Publish-Build-Assets - - group: AzureDevOps-Artifact-Feeds-Pats - - name: runCodesignValidationInjection - value: false - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates-official/post-build/common-variables.yml - - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 - os: windows - steps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download artifact - inputs: - artifactName: AssetManifests - downloadPath: '$(Build.StagingDirectory)/Download' - checkDownloadedFiles: true - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: NuGetAuthenticate@1 - - - task: PowerShell@2 - displayName: Publish Build Assets - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com - /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} - /p:OfficialBuildId=$(Build.BuildNumber) - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: powershell@2 - displayName: Create ReleaseConfigs Artifact - inputs: - targetType: inline - script: | - New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force - $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" - Add-Content -Path $filePath -Value $(BARBuildId) - Add-Content -Path $filePath -Value "$(DefaultChannels)" - Add-Content -Path $filePath -Value $(IsStableBuild) - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish ReleaseConfigs Artifact - inputs: - PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - task: powershell@2 - displayName: Check if SymbolPublishingExclusionsFile.txt exists - inputs: - targetType: inline - script: | - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" - if(Test-Path -Path $symbolExclusionfile) - { - Write-Host "SymbolExclusionFile exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" - } - else{ - Write-Host "Symbols Exclusion file does not exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" - } - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish SymbolPublishingExclusionsFile Artifact - condition: eq(variables['SymbolExclusionFile'], 'true') - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates-official/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion 3 - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: true - - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - - template: /eng/common/templates-official/steps/publish-logs.yml - parameters: - JobLabel: 'Publish_Artifacts_Logs' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml index f193dfbe23668..1a480034b678e 100644 --- a/eng/common/templates-official/job/source-build.yml +++ b/eng/common/templates-official/job/source-build.yml @@ -1,67 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. The template produces a server job with a - # default ID 'Source_Build_Complete' to put in a dependency list if necessary. - - # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. - jobNamePrefix: 'Source_Build' - - # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for - # managed-only repositories. This is an object with these properties: - # - # name: '' - # The name of the job. This is included in the job ID. - # targetRID: '' - # The name of the target RID to use, instead of the one auto-detected by Arcade. - # nonPortable: false - # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than - # linux-x64), and compiling against distro-provided packages rather than portable ones. - # skipPublishValidation: false - # Disables publishing validation. By default, a check is performed to ensure no packages are - # published by source-build. - # container: '' - # A container to use. Runs in docker. - # pool: {} - # A pool to use. Runs directly on an agent. - # buildScript: '' - # Specifies the build script to invoke to perform the build in the repo. The default - # './build.sh' should work for typical Arcade repositories, but this is customizable for - # difficult situations. - # jobProperties: {} - # A list of job properties to inject at the top level, for potential extensibility beyond - # container and pool. - platform: {} - jobs: -- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} - displayName: Source-Build (${{ parameters.platform.name }}) - - ${{ each property in parameters.platform.jobProperties }}: - ${{ property.key }}: ${{ property.value }} - - ${{ if ne(parameters.platform.container, '') }}: - container: ${{ parameters.platform.container }} - - ${{ if eq(parameters.platform.pool, '') }}: - # The default VM host AzDO pool. This should be capable of running Docker containers: almost all - # source-build builds run in Docker, including the default managed platform. - # /eng/common/templates-official/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - image: 1es-mariner-2 - os: linux - - ${{ if ne(parameters.platform.pool, '') }}: - pool: ${{ parameters.platform.pool }} - - workspace: - clean: all +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: true - steps: - - template: /eng/common/templates-official/steps/source-build.yml - parameters: - platform: ${{ parameters.platform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml index f0513aee5b0da..6d5ead316f92b 100644 --- a/eng/common/templates-official/job/source-index-stage1.yml +++ b/eng/common/templates-official/job/source-index-stage1.yml @@ -1,68 +1,7 @@ -parameters: - runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 - sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json - sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" - preSteps: [] - binlogPath: artifacts/log/Debug/Build.binlog - condition: '' - dependsOn: '' - pool: '' - jobs: -- job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} - variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} - - name: SourceIndexPackageSource - value: ${{ parameters.sourceIndexPackageSource }} - - name: BinlogPath - value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $(DncEngPublicBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64.open - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 - os: windows - - steps: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 - inputs: - packageType: sdk - version: 6.0.x - installationPath: $(Agent.TempDirectory)/dotnet - workingDirectory: $(Agent.TempDirectory) - - - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - displayName: Download Tools - # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. - workingDirectory: $(Agent.TempDirectory) - - - script: ${{ parameters.sourceIndexBuildCommand }} - displayName: Build Repository - - - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output - displayName: Process Binlog into indexable sln +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: true - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) - displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/codeql-build.yml b/eng/common/templates-official/jobs/codeql-build.yml index b68d3c2f31990..a726322ecfe01 100644 --- a/eng/common/templates-official/jobs/codeql-build.yml +++ b/eng/common/templates-official/jobs/codeql-build.yml @@ -1,31 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - jobs: -- template: /eng/common/templates-official/jobs/jobs.yml +- template: /eng/common/core-templates/jobs/codeql-build.yml parameters: - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishTestResults: false - enablePublishBuildAssets: false - enablePublishUsingPipelines: false - enableTelemetry: true + is1ESPipeline: true - variables: - - group: Publish-Build-Assets - # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in - # sync with the packages.config file. - - name: DefaultGuardianVersion - value: 0.109.0 - - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - jobs: ${{ parameters.jobs }} - + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/jobs.yml b/eng/common/templates-official/jobs/jobs.yml index 857a0f8ba43e8..007deddaea0f5 100644 --- a/eng/common/templates-official/jobs/jobs.yml +++ b/eng/common/templates-official/jobs/jobs.yml @@ -1,97 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: Enable publishing using release pipelines - enablePublishUsingPipelines: false - - # Optional: Enable running the source-build jobs to build repo from source - enableSourceBuild: false - - # Optional: Parameters for source-build template. - # See /eng/common/templates-official/jobs/source-build.yml for options - sourceBuildParameters: [] - - graphFileGeneration: - # Optional: Enable generating the graph files at the end of the build - enabled: false - # Optional: Include toolset dependencies in the generated graph files - includeToolset: false - - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - - # Optional: Override automatically derived dependsOn value for "publish build assets" job - publishBuildAssetsDependsOn: '' - - # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. - publishAssetsImmediately: false - - # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) - artifactsPublishingAdditionalParameters: '' - signingValidationAdditionalParameters: '' - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - enableSourceIndex: false - sourceIndexParams: {} - -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - jobs: -- ${{ each job in parameters.jobs }}: - - template: ../job/job.yml - parameters: - # pass along parameters - ${{ each parameter in parameters }}: - ${{ if ne(parameter.key, 'jobs') }}: - ${{ parameter.key }}: ${{ parameter.value }} - - # pass along job properties - ${{ each property in job }}: - ${{ if ne(property.key, 'job') }}: - ${{ property.key }}: ${{ property.value }} - - name: ${{ job.job }} - -- ${{ if eq(parameters.enableSourceBuild, true) }}: - - template: /eng/common/templates-official/jobs/source-build.yml - parameters: - allCompletedJobId: Source_Build_Complete - ${{ each parameter in parameters.sourceBuildParameters }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if eq(parameters.enableSourceIndex, 'true') }}: - - template: ../job/source-index-stage1.yml - parameters: - runAsPublic: ${{ parameters.runAsPublic }} - ${{ each parameter in parameters.sourceIndexParams }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: - - template: ../job/publish-build-assets.yml - parameters: - continueOnError: ${{ parameters.continueOnError }} - dependsOn: - - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.publishBuildAssetsDependsOn }}: - - ${{ job.job }} - - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.jobs }}: - - ${{ job.job }} - - ${{ if eq(parameters.enableSourceBuild, true) }}: - - Source_Build_Complete +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: true - runAsPublic: ${{ parameters.runAsPublic }} - publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} - publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} - enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/source-build.yml b/eng/common/templates-official/jobs/source-build.yml index 08e5db9bb1161..483e7b611f346 100644 --- a/eng/common/templates-official/jobs/source-build.yml +++ b/eng/common/templates-official/jobs/source-build.yml @@ -1,46 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. A job is created for each platform, as - # well as an optional server job that completes when all platform jobs complete. - - # The name of the "join" job for all source-build platforms. If set to empty string, the job is - # not included. Existing repo pipelines can use this job depend on all source-build jobs - # completing without maintaining a separate list of every single job ID: just depend on this one - # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. - allCompletedJobId: '' - - # See /eng/common/templates-official/job/source-build.yml - jobNamePrefix: 'Source_Build' - - # This is the default platform provided by Arcade, intended for use by a managed-only repo. - defaultManagedPlatform: - name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' - - # Defines the platforms on which to run build jobs. One job is created for each platform, and the - # object in this array is sent to the job template as 'platform'. If no platforms are specified, - # one job runs on 'defaultManagedPlatform'. - platforms: [] - jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: true -- ${{ if ne(parameters.allCompletedJobId, '') }}: - - job: ${{ parameters.allCompletedJobId }} - displayName: Source-Build Complete - pool: server - dependsOn: - - ${{ each platform in parameters.platforms }}: - - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(length(parameters.platforms), 0) }}: - - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - -- ${{ each platform in parameters.platforms }}: - - template: /eng/common/templates-official/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ platform }} - -- ${{ if eq(length(parameters.platforms), 0) }}: - - template: /eng/common/templates-official/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ parameters.defaultManagedPlatform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/common-variables.yml b/eng/common/templates-official/post-build/common-variables.yml index c24193acfc981..c32fc49233f8f 100644 --- a/eng/common/templates-official/post-build/common-variables.yml +++ b/eng/common/templates-official/post-build/common-variables.yml @@ -1,22 +1,8 @@ variables: - - group: Publish-Build-Assets +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - # Whether the build is internal or not - - name: IsInternalBuild - value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} - - # Default Maestro++ API Endpoint and API Version - - name: MaestroApiEndPoint - value: "https://maestro-prod.westus2.cloudapp.azure.com" - - name: MaestroApiAccessToken - value: $(MaestroAccessToken) - - name: MaestroApiVersion - value: "2020-02-20" - - - name: SourceLinkCLIVersion - value: 3.0.0 - - name: SymbolToolVersion - value: 1.0.1 - - - name: runCodesignValidationInjection - value: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml index da1f40958b450..2364c0fd4a527 100644 --- a/eng/common/templates-official/post-build/post-build.yml +++ b/eng/common/templates-official/post-build/post-build.yml @@ -1,285 +1,8 @@ -parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - stages: -- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - - stage: Validate - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Validate Build Assets - variables: - - template: common-variables.yml - - template: /eng/common/templates-official/variables/pool-providers.yml - jobs: - - job: - displayName: NuGet Validation - condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ - - - job: - displayName: Signing Validation - condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - itemPattern: | - ** - !**/Microsoft.SourceBuild.Intermediate.*.nupkg - - # This is necessary whenever we want to publish/restore to an AzDO private feed - # Since sdk-task.ps1 tries to restore packages we need to do this authentication here - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: ../steps/publish-logs.yml - parameters: - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) - - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true - -- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - - stage: publish_using_darc - ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - dependsOn: ${{ parameters.publishDependsOn }} - ${{ else }}: - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Publish using Darc - variables: - - template: common-variables.yml - - template: /eng/common/templates-official/variables/pool-providers.yml - jobs: - - job: - displayName: Publish Using Darc - timeoutInMinutes: 120 - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: NuGetAuthenticate@1 +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/post-build/setup-maestro-vars.yml b/eng/common/templates-official/post-build/setup-maestro-vars.yml index 0c87f149a4ad7..024397d878645 100644 --- a/eng/common/templates-official/post-build/setup-maestro-vars.yml +++ b/eng/common/templates-official/post-build/setup-maestro-vars.yml @@ -1,70 +1,8 @@ -parameters: - BARBuildId: '' - PromoteToChannelIds: '' - steps: - - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Release Configs - inputs: - buildType: current - artifactName: ReleaseConfigs - checkDownloadedFiles: true - - - task: PowerShell@2 - name: setReleaseVars - displayName: Set Release Configs Vars - inputs: - targetType: inline - pwsh: true - script: | - try { - if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { - $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt - - $BarId = $Content | Select -Index 0 - $Channels = $Content | Select -Index 1 - $IsStableBuild = $Content | Select -Index 2 - - $AzureDevOpsProject = $Env:System_TeamProject - $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId - $AzureDevOpsBuildId = $Env:Build_BuildId - } - else { - $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" - - $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $apiHeaders.Add('Accept', 'application/json') - $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") - - $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } - - $BarId = $Env:BARBuildId - $Channels = $Env:PromoteToMaestroChannels -split "," - $Channels = $Channels -join "][" - $Channels = "[$Channels]" - - $IsStableBuild = $buildInfo.stable - $AzureDevOpsProject = $buildInfo.azureDevOpsProject - $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId - $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId - } - - Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" - Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" - Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" - Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" - Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" - } - catch { - Write-Host $_ - Write-Host $_.Exception - Write-Host $_.ScriptStackTrace - exit 1 - } - env: - MAESTRO_API_TOKEN: $(MaestroApiAccessToken) - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/add-build-to-channel.yml b/eng/common/templates-official/steps/add-build-to-channel.yml index f67a210d62f3e..543dea8c6969a 100644 --- a/eng/common/templates-official/steps/add-build-to-channel.yml +++ b/eng/common/templates-official/steps/add-build-to-channel.yml @@ -1,13 +1,7 @@ -parameters: - ChannelId: 0 - steps: -- task: PowerShell@2 - displayName: Add Build to Channel - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 - arguments: -BuildId $(BARBuildId) - -ChannelId ${{ parameters.ChannelId }} - -MaestroApiAccessToken $(MaestroApiAccessToken) - -MaestroApiEndPoint $(MaestroApiEndPoint) - -MaestroApiVersion $(MaestroApiVersion) +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/build-reason.yml b/eng/common/templates-official/steps/build-reason.yml deleted file mode 100644 index eba58109b52c9..0000000000000 --- a/eng/common/templates-official/steps/build-reason.yml +++ /dev/null @@ -1,12 +0,0 @@ -# build-reason.yml -# Description: runs steps if build.reason condition is valid. conditions is a string of valid build reasons -# to include steps (',' separated). -parameters: - conditions: '' - steps: [] - -steps: - - ${{ if and( not(startsWith(parameters.conditions, 'not')), contains(parameters.conditions, variables['build.reason'])) }}: - - ${{ parameters.steps }} - - ${{ if and( startsWith(parameters.conditions, 'not'), not(contains(parameters.conditions, variables['build.reason']))) }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates-official/steps/component-governance.yml b/eng/common/templates-official/steps/component-governance.yml index cbba0596709da..30bb3985ca2bf 100644 --- a/eng/common/templates-official/steps/component-governance.yml +++ b/eng/common/templates-official/steps/component-governance.yml @@ -1,13 +1,7 @@ -parameters: - disableComponentGovernance: false - componentGovernanceIgnoreDirectories: '' - steps: -- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: - - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" - displayName: Set skipComponentGovernanceDetection variable -- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true - inputs: - ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/execute-codeql.yml b/eng/common/templates-official/steps/execute-codeql.yml deleted file mode 100644 index 9b4a5ffa30a78..0000000000000 --- a/eng/common/templates-official/steps/execute-codeql.yml +++ /dev/null @@ -1,32 +0,0 @@ -parameters: - # Language that should be analyzed. Defaults to csharp - language: csharp - # Build Commands - buildCommands: '' - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - # optional: determines whether to continue the build if the step errors; - sdlContinueOnError: false - -steps: -- template: /eng/common/templates-official/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: '${{ parameters.additionalParameters }} - -CodeQLAdditionalRunConfigParams @("BuildCommands < ${{ parameters.buildCommands }}", "Language < ${{ parameters.language }}")' - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/execute-sdl.yml b/eng/common/templates-official/steps/execute-sdl.yml deleted file mode 100644 index 07426fde05d82..0000000000000 --- a/eng/common/templates-official/steps/execute-sdl.yml +++ /dev/null @@ -1,88 +0,0 @@ -parameters: - overrideGuardianVersion: '' - executeAllSdlToolsScript: '' - overrideParameters: '' - additionalParameters: '' - publishGuardianDirectoryToPipeline: false - sdlContinueOnError: false - condition: '' - -steps: -- task: NuGetAuthenticate@1 - inputs: - nuGetServiceConnections: GuardianConnect - -- task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - -- ${{ if ne(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts -Version ${{ parameters.overrideGuardianVersion }} - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian (Overridden) - -- ${{ if eq(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian - -- ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} - displayName: Execute SDL (Overridden) - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} - -GuardianCliLocation $(GuardianCliLocation) - -NugetPackageDirectory $(Build.SourcesDirectory)\.packages - -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) - ${{ parameters.additionalParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: - # We want to publish the Guardian results and configuration for easy diagnosis. However, the - # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default - # tooling files. Some of these files are large and aren't useful during an investigation, so - # exclude them by simply deleting them before publishing. (As of writing, there is no documented - # way to selectively exclude a dir from the pipeline artifact publish task.) - - task: DeleteFiles@1 - displayName: Delete Guardian dependencies to avoid uploading - inputs: - SourceFolder: $(Agent.BuildDirectory)/.gdn - Contents: | - c - i - condition: succeededOrFailed() - - - publish: $(Agent.BuildDirectory)/.gdn - artifact: GuardianConfiguration - displayName: Publish GuardianConfiguration - condition: succeededOrFailed() - - # Publish the SARIF files in a container named CodeAnalysisLogs to enable integration - # with the "SARIF SAST Scans Tab" Azure DevOps extension - - task: CopyFiles@2 - displayName: Copy SARIF files - inputs: - flattenFolders: true - sourceFolder: $(Agent.BuildDirectory)/.gdn/rc/ - contents: '**/*.sarif' - targetFolder: $(Build.SourcesDirectory)/CodeAnalysisLogs - condition: succeededOrFailed() - - # Use PublishBuildArtifacts because the SARIF extension only checks this case - # see microsoft/sarif-azuredevops-extension#4 - - task: PublishBuildArtifacts@1 - displayName: Publish SARIF files to CodeAnalysisLogs container - inputs: - pathToPublish: $(Build.SourcesDirectory)/CodeAnalysisLogs - artifactName: CodeAnalysisLogs - condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/common/templates-official/steps/generate-sbom.yml b/eng/common/templates-official/steps/generate-sbom.yml index 1bf43bf807af3..9a89a4706d94e 100644 --- a/eng/common/templates-official/steps/generate-sbom.yml +++ b/eng/common/templates-official/steps/generate-sbom.yml @@ -1,48 +1,7 @@ -# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. -# PackageName - The name of the package this SBOM represents. -# PackageVersion - The version of the package this SBOM represents. -# ManifestDirPath - The path of the directory where the generated manifest files will be placed -# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. - -parameters: - PackageVersion: 8.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - PackageName: '.NET' - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - IgnoreDirectories: '' - sbomContinueOnError: true - steps: -- task: PowerShell@2 - displayName: Prep for SBOM generation in (Non-linux) - condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) - inputs: - filePath: ./eng/common/generate-sbom-prep.ps1 - arguments: ${{parameters.manifestDirPath}} - -# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 -- script: | - chmod +x ./eng/common/generate-sbom-prep.sh - ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} - displayName: Prep for SBOM generation in (Linux) - condition: eq(variables['Agent.Os'], 'Linux') - continueOnError: ${{ parameters.sbomContinueOnError }} - -- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: 'Generate SBOM manifest' - continueOnError: ${{ parameters.sbomContinueOnError }} - inputs: - PackageName: ${{ parameters.packageName }} - BuildDropPath: ${{ parameters.buildDropPath }} - PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }} - ${{ if ne(parameters.IgnoreDirectories, '') }}: - AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' - -- task: 1ES.PublishPipelineArtifact@1 - displayName: Publish SBOM manifest - continueOnError: ${{parameters.sbomContinueOnError}} - inputs: - targetPath: '${{parameters.manifestDirPath}}' - artifactName: $(ARTIFACT_NAME) +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: true + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-build-artifacts.yml b/eng/common/templates-official/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..100a3fc98493c --- /dev/null +++ b/eng/common/templates-official/steps/publish-build-artifacts.yml @@ -0,0 +1,41 @@ +parameters: +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +- name: is1ESPipeline + type: boolean + default: true + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} + diff --git a/eng/common/templates-official/steps/publish-logs.yml b/eng/common/templates-official/steps/publish-logs.yml index 04012fed182a1..579fd531e94c3 100644 --- a/eng/common/templates-official/steps/publish-logs.yml +++ b/eng/common/templates-official/steps/publish-logs.yml @@ -1,23 +1,7 @@ -parameters: - StageLabel: '' - JobLabel: '' - steps: -- task: Powershell@2 - displayName: Prepare Binlogs to Upload - inputs: - targetType: inline - script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - continueOnError: true - condition: always() +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: true -- task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/PostBuildLogs' - PublishLocation: Container - ArtifactName: PostBuildLogs - continueOnError: true - condition: always() + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..d71eb0c743986 --- /dev/null +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,26 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: true + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/retain-build.yml b/eng/common/templates-official/steps/retain-build.yml index 83d97a26a01ff..5594551508a3c 100644 --- a/eng/common/templates-official/steps/retain-build.yml +++ b/eng/common/templates-official/steps/retain-build.yml @@ -1,28 +1,7 @@ -parameters: - # Optional azure devops PAT with build execute permissions for the build's organization, - # only needed if the build that should be retained ran on a different organization than - # the pipeline where this template is executing from - Token: '' - # Optional BuildId to retain, defaults to the current running build - BuildId: '' - # Azure devops Organization URI for the build in the https://dev.azure.com/ format. - # Defaults to the organization the current pipeline is running on - AzdoOrgUri: '$(System.CollectionUri)' - # Azure devops project for the build. Defaults to the project the current pipeline is running on - AzdoProject: '$(System.TeamProject)' - steps: - - task: powershell@2 - inputs: - targetType: 'filePath' - filePath: eng/common/retain-build.ps1 - pwsh: true - arguments: > - -AzdoOrgUri: ${{parameters.AzdoOrgUri}} - -AzdoProject ${{parameters.AzdoProject}} - -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} - -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} - displayName: Enable permanent build retention - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - BUILD_ID: $(Build.BuildId) \ No newline at end of file +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/send-to-helix.yml b/eng/common/templates-official/steps/send-to-helix.yml index 3eb7e2d5f840c..6500f21bf845c 100644 --- a/eng/common/templates-official/steps/send-to-helix.yml +++ b/eng/common/templates-official/steps/send-to-helix.yml @@ -1,91 +1,7 @@ -# Please remember to update the documentation if you make changes to these parameters! -parameters: - HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' - HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number - HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues - HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group - HelixConfiguration: '' # optional -- additional property attached to a job - HelixPreCommands: '' # optional -- commands to run before Helix work item execution - HelixPostCommands: '' # optional -- commands to run after Helix work item execution - WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects - WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects - WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects - CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload - XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true - XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects - XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects - XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner - XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects - IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." - IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set - HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) - Creator: '' # optional -- if the build is external, use this to specify who is sending the job - DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO - condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() - continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false - steps: - - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' - displayName: ${{ parameters.DisplayNamePrefix }} (Windows) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog - displayName: ${{ parameters.DisplayNamePrefix }} (Unix) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/source-build.yml b/eng/common/templates-official/steps/source-build.yml index 829f17c34d118..8f92c49e7b06f 100644 --- a/eng/common/templates-official/steps/source-build.yml +++ b/eng/common/templates-official/steps/source-build.yml @@ -1,129 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. - - # This is a 'steps' template, and is intended for advanced scenarios where the existing build - # infra has a careful build methodology that must be followed. For example, a repo - # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline - # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to - # GitHub. Using this steps template leaves room for that infra to be included. - - # Defines the platform on which to run the steps. See 'eng/common/templates-official/job/source-build.yml' - # for details. The entire object is described in the 'job' template for simplicity, even though - # the usage of the properties on this object is split between the 'job' and 'steps' templates. - platform: {} - steps: -# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) -- script: | - set -x - df -h - - # If building on the internal project, the artifact feeds variable may be available (usually only if needed) - # In that case, call the feed setup script to add internal feeds corresponding to public ones. - # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. - # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those - # changes. - internalRestoreArgs= - if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then - # Temporarily work around https://github.com/dotnet/arcade/issues/7709 - chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) - internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. - # This only works if there is a username/email configured, which won't be the case in most CI runs. - git config --get user.email - if [ $? -ne 0 ]; then - git config user.email dn-bot@microsoft.com - git config user.name dn-bot - fi - fi - - # If building on the internal project, the internal storage variable may be available (usually only if needed) - # In that case, add variables to allow the download of internal runtimes if the specified versions are not found - # in the default public locations. - internalRuntimeDownloadArgs= - if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' - fi - - buildConfig=Release - # Check if AzDO substitutes in a build config from a variable, and use it if so. - if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then - buildConfig='$(_BuildConfig)' - fi - - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - - targetRidArgs= - if [ '${{ parameters.platform.targetRID }}' != '' ]; then - targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' - fi - - runtimeOsArgs= - if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then - runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' - fi - - baseOsArgs= - if [ '${{ parameters.platform.baseOS }}' != '' ]; then - baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' - fi - - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - - ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ - --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ - $officialBuildArgs \ - $internalRuntimeDownloadArgs \ - $internalRestoreArgs \ - $targetRidArgs \ - $runtimeOsArgs \ - $baseOsArgs \ - /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ - /p:ArcadeBuildFromSource=true \ - /p:AssetManifestFileName=$assetManifestFileName - displayName: Build - -# Upload build logs for diagnosis. -- task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(Build.SourcesDirectory)' - Contents: | - **/*.log - **/*.binlog - artifacts/source-build/self/prebuilt-report/** - TargetFolder: '$(Build.StagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - -- task: 1ES.PublishPipelineArtifact@1 - displayName: Publish BuildLogs - inputs: - targetPath: '$(Build.StagingDirectory)/BuildLogs' - artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) - continueOnError: true - condition: succeededOrFailed() +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: true -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- task: ComponentGovernanceComponentDetection@0 - displayName: Component Detection (Exclude upstream cache) - inputs: - ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml deleted file mode 100644 index 7870f93bc1765..0000000000000 --- a/eng/common/templates/job/execute-sdl.yml +++ /dev/null @@ -1,139 +0,0 @@ -parameters: - enable: 'false' # Whether the SDL validation job should execute or not - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - sdlContinueOnError: false # optional: determines whether to continue the build if the step errors; - # optional: determines if build artifacts should be downloaded. - downloadArtifacts: true - # optional: determines if this job should search the directory of downloaded artifacts for - # 'tar.gz' and 'zip' archive files and extract them before running SDL validation tasks. - extractArchiveArtifacts: false - dependsOn: '' # Optional: dependencies of the job - artifactNames: '' # Optional: patterns supplied to DownloadBuildArtifacts - # Usage: - # artifactNames: - # - 'BlobArtifacts' - # - 'Artifacts_Windows_NT_Release' - # Optional: download a list of pipeline artifacts. 'downloadArtifacts' controls build artifacts, - # not pipeline artifacts, so doesn't affect the use of this parameter. - pipelineArtifactNames: [] - -jobs: -- job: Run_SDL - dependsOn: ${{ parameters.dependsOn }} - displayName: Run SDL tool - condition: and(succeededOrFailed(), eq( ${{ parameters.enable }}, 'true')) - variables: - - group: DotNet-VSTS-Bot - - name: AzDOProjectName - value: ${{ parameters.AzDOProjectName }} - - name: AzDOPipelineId - value: ${{ parameters.AzDOPipelineId }} - - name: AzDOBuildId - value: ${{ parameters.AzDOBuildId }} - - template: /eng/common/templates/variables/sdl-variables.yml - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - template: /eng/common/templates/variables/pool-providers.yml - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - checkout: self - clean: true - - # If the template caller didn't provide an AzDO parameter, set them all up as Maestro vars. - - ${{ if not(and(parameters.AzDOProjectName, parameters.AzDOPipelineId, parameters.AzDOBuildId)) }}: - - template: /eng/common/templates/post-build/setup-maestro-vars.yml - - - ${{ if ne(parameters.downloadArtifacts, 'false')}}: - - ${{ if ne(parameters.artifactNames, '') }}: - - ${{ each artifactName in parameters.artifactNames }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Build Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: ${{ artifactName }} - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.artifactNames, '') }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Build Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - downloadType: specific files - itemPattern: "**" - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - - ${{ each artifactName in parameters.pipelineArtifactNames }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: ${{ artifactName }} - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - - powershell: eng/common/sdl/trim-assets-version.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts - displayName: Trim the version from the NuGet packages - continueOnError: ${{ parameters.sdlContinueOnError }} - - - powershell: eng/common/sdl/extract-artifact-packages.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts - displayName: Extract Blob Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - powershell: eng/common/sdl/extract-artifact-packages.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts - displayName: Extract Package Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: - - powershell: eng/common/sdl/extract-artifact-archives.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts - displayName: Extract Archive Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - template: /eng/common/templates/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: ${{ parameters.additionalParameters }} - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 8ec5c4f2d9f91..1cf9a6d48127b 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -1,259 +1,61 @@ -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - -parameters: -# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - cancelTimeoutInMinutes: '' - condition: '' - container: '' - continueOnError: false - dependsOn: '' - displayName: '' - pool: '' - steps: [] - strategy: '' - timeoutInMinutes: '' - variables: [] - workspace: '' - templateContext: '' - -# Job base template specific parameters - # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md - artifacts: '' - enableMicrobuild: false +parameters: enablePublishBuildArtifacts: false - enablePublishBuildAssets: false - enablePublishTestResults: false - enablePublishUsingPipelines: false - enableBuildRetry: false - disableComponentGovernance: '' - componentGovernanceIgnoreDirectories: '' - mergeTestResults: false - testRunTitle: '' - testResultsFormat: '' - name: '' - preSteps: [] - runAsPublic: false -# Sbom related params - enableSbom: true - PackageVersion: 7.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' jobs: -- job: ${{ parameters.name }} - - ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: - cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} - - ${{ if ne(parameters.condition, '') }}: - condition: ${{ parameters.condition }} - - ${{ if ne(parameters.container, '') }}: - container: ${{ parameters.container }} - - ${{ if ne(parameters.continueOnError, '') }}: - continueOnError: ${{ parameters.continueOnError }} - - ${{ if ne(parameters.dependsOn, '') }}: - dependsOn: ${{ parameters.dependsOn }} - - ${{ if ne(parameters.displayName, '') }}: - displayName: ${{ parameters.displayName }} - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - - ${{ if ne(parameters.strategy, '') }}: - strategy: ${{ parameters.strategy }} - - ${{ if ne(parameters.timeoutInMinutes, '') }}: - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - ${{ if ne(parameters.templateContext, '') }}: - templateContext: ${{ parameters.templateContext }} - - variables: - - ${{ if ne(parameters.enableTelemetry, 'false') }}: - - name: DOTNET_CLI_TELEMETRY_PROFILE - value: '$(Build.Repository.Uri)' - - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: - - name: EnableRichCodeNavigation - value: 'true' - # Retry signature validation up to three times, waiting 2 seconds between attempts. - # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures - - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY - value: 3,2000 - - ${{ each variable in parameters.variables }}: - # handle name-value variable syntax - # example: - # - name: [key] - # value: [value] - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - # handle variable groups - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - # handle template variable syntax - # example: - # - template: path/to/template.yml - # parameters: - # [key]: [value] - - ${{ if ne(variable.template, '') }}: - - template: ${{ variable.template }} - ${{ if ne(variable.parameters, '') }}: - parameters: ${{ variable.parameters }} - - # handle key-value variable syntax. - # example: - # - [key]: [value] - - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: - - ${{ each pair in variable }}: - - name: ${{ pair.key }} - value: ${{ pair.value }} - - # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds - - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: DotNet-HelixApi-Access - - ${{ if ne(parameters.workspace, '') }}: - workspace: ${{ parameters.workspace }} - - steps: - - ${{ if ne(parameters.preSteps, '') }}: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - task: MicroBuildSigningPlugin@3 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - env: - TeamName: $(_TeamName) - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - - - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@1 - - - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: current - artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} - targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} - itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} - - - ${{ each step in parameters.steps }}: - - ${{ step }} - - - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} - environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }} - richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin - uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} - continueOnError: true - - - template: /eng/common/templates/steps/component-governance.yml - parameters: - ${{ if eq(parameters.disableComponentGovernance, '') }}: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: - disableComponentGovernance: false - ${{ else }}: - disableComponentGovernance: true - ${{ else }}: - disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks - condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - env: - TeamName: $(_TeamName) - - - ${{ if ne(parameters.artifacts.publish, '') }}: - - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - task: CopyFiles@2 - displayName: Gather binaries for publish to artifacts - inputs: - SourceFolder: 'artifacts/bin' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' - - task: CopyFiles@2 - displayName: Gather packages for publish to artifacts - inputs: - SourceFolder: 'artifacts/packages' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' - - task: PublishBuildArtifacts@1 - displayName: Publish pipeline artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} - continueOnError: true - condition: always() - - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - - publish: artifacts/log - artifact: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} - displayName: Publish logs - continueOnError: true - condition: always() - - - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - - task: PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} - continueOnError: true - condition: always() - - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: - - task: PublishTestResults@2 - displayName: Publish XUnit Test Results - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: - - task: PublishTestResults@2 - displayName: Publish TRX Test Results - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - template: /eng/common/templates/steps/generate-sbom.yml - parameters: - PackageVersion: ${{ parameters.packageVersion}} - BuildDropPath: ${{ parameters.buildDropPath }} - IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - - publish: $(Build.SourcesDirectory)\eng\common\BuildConfiguration - artifact: BuildConfiguration - displayName: Publish build retry configuration - continueOnError: true +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'steps'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} + + steps: + - ${{ each step in parameters.steps }}: + - ${{ step }} + + artifactPublishSteps: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish pipeline artifacts + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + publishLocation: Container + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + continueOnError: true + condition: always() + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml index 60ab00c4de3ac..ff829dc4c700c 100644 --- a/eng/common/templates/job/onelocbuild.yml +++ b/eng/common/templates/job/onelocbuild.yml @@ -1,109 +1,7 @@ -parameters: - # Optional: dependencies of the job - dependsOn: '' - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: '' - - CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex - GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - - SourcesDirectory: $(Build.SourcesDirectory) - CreatePr: true - AutoCompletePr: false - ReusePr: true - UseLfLineEndings: true - UseCheckedInLocProjectJson: false - SkipLocProjectJsonGeneration: false - LanguageSet: VS_Main_Languages - LclSource: lclFilesInRepo - LclPackageId: '' - RepoType: gitHub - GitHubOrg: dotnet - MirrorRepo: '' - MirrorBranch: main - condition: '' - JobNameSuffix: '' - jobs: -- job: OneLocBuild${{ parameters.JobNameSuffix }} - - dependsOn: ${{ parameters.dependsOn }} - - displayName: OneLocBuild${{ parameters.JobNameSuffix }} - - variables: - - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat - - name: _GenerateLocProjectArguments - value: -SourcesDirectory ${{ parameters.SourcesDirectory }} - -LanguageSet "${{ parameters.LanguageSet }}" - -CreateNeutralXlfs - - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: - - name: _GenerateLocProjectArguments - value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - - task: Powershell@2 - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 - arguments: $(_GenerateLocProjectArguments) - displayName: Generate LocProject.json - condition: ${{ parameters.condition }} - - - task: OneLocBuild@2 - displayName: OneLocBuild - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - locProj: eng/Localize/LocProject.json - outDir: $(Build.ArtifactStagingDirectory) - lclSource: ${{ parameters.LclSource }} - lclPackageId: ${{ parameters.LclPackageId }} - isCreatePrSelected: ${{ parameters.CreatePr }} - isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} - ${{ if eq(parameters.CreatePr, true) }}: - isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - isShouldReusePrSelected: ${{ parameters.ReusePr }} - packageSourceAuth: patAuth - patVariable: ${{ parameters.CeapexPat }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - repoType: ${{ parameters.RepoType }} - gitHubPatVariable: "${{ parameters.GithubPat }}" - ${{ if ne(parameters.MirrorRepo, '') }}: - isMirrorRepoSelected: true - gitHubOrganization: ${{ parameters.GitHubOrg }} - mirrorRepo: ${{ parameters.MirrorRepo }} - mirrorBranch: ${{ parameters.MirrorBranch }} - condition: ${{ parameters.condition }} - - - task: PublishBuildArtifacts@1 - displayName: Publish Localization Files - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: false - - task: PublishBuildArtifacts@1 - displayName: Publish LocProject.json - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 8ec0151def21a..ab2edec2adb54 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -1,151 +1,7 @@ -parameters: - configuration: 'Debug' - - # Optional: condition for the job to run - condition: '' - - # Optional: 'true' if future jobs should run even if this job fails - continueOnError: false - - # Optional: dependencies of the job - dependsOn: '' - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: {} - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishUsingPipelines: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishAssetsImmediately: false - - artifactsPublishingAdditionalParameters: '' - - signingValidationAdditionalParameters: '' - jobs: -- job: Asset_Registry_Publish - - dependsOn: ${{ parameters.dependsOn }} - timeoutInMinutes: 150 - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - displayName: Publish Assets - ${{ else }}: - displayName: Publish to Build Asset Registry - - variables: - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: Publish-Build-Assets - - group: AzureDevOps-Artifact-Feeds-Pats - - name: runCodesignValidationInjection - value: false - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates/post-build/common-variables.yml - - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download artifact - inputs: - artifactName: AssetManifests - downloadPath: '$(Build.StagingDirectory)/Download' - checkDownloadedFiles: true - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: NuGetAuthenticate@1 - - - task: PowerShell@2 - displayName: Publish Build Assets - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro.dot.net - /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} - /p:OfficialBuildId=$(Build.BuildNumber) - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: powershell@2 - displayName: Create ReleaseConfigs Artifact - inputs: - targetType: inline - script: | - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId) - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)" - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild) - - - task: PublishBuildArtifacts@1 - displayName: Publish ReleaseConfigs Artifact - inputs: - PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - task: powershell@2 - displayName: Check if SymbolPublishingExclusionsFile.txt exists - inputs: - targetType: inline - script: | - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" - if(Test-Path -Path $symbolExclusionfile) - { - Write-Host "SymbolExclusionFile exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" - } - else{ - Write-Host "Symbols Exclusion file does not exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" - } - - - task: PublishBuildArtifacts@1 - displayName: Publish SymbolPublishingExclusionsFile Artifact - condition: eq(variables['SymbolExclusionFile'], 'true') - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion 3 - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: false - - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - - template: /eng/common/templates/steps/publish-logs.yml - parameters: - JobLabel: 'Publish_Artifacts_Logs' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml index 8a3deef2b7274..e44d47b1d760c 100644 --- a/eng/common/templates/job/source-build.yml +++ b/eng/common/templates/job/source-build.yml @@ -1,66 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. The template produces a server job with a - # default ID 'Source_Build_Complete' to put in a dependency list if necessary. - - # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. - jobNamePrefix: 'Source_Build' - - # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for - # managed-only repositories. This is an object with these properties: - # - # name: '' - # The name of the job. This is included in the job ID. - # targetRID: '' - # The name of the target RID to use, instead of the one auto-detected by Arcade. - # nonPortable: false - # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than - # linux-x64), and compiling against distro-provided packages rather than portable ones. - # skipPublishValidation: false - # Disables publishing validation. By default, a check is performed to ensure no packages are - # published by source-build. - # container: '' - # A container to use. Runs in docker. - # pool: {} - # A pool to use. Runs directly on an agent. - # buildScript: '' - # Specifies the build script to invoke to perform the build in the repo. The default - # './build.sh' should work for typical Arcade repositories, but this is customizable for - # difficult situations. - # jobProperties: {} - # A list of job properties to inject at the top level, for potential extensibility beyond - # container and pool. - platform: {} - jobs: -- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} - displayName: Source-Build (${{ parameters.platform.name }}) - - ${{ each property in parameters.platform.jobProperties }}: - ${{ property.key }}: ${{ property.value }} - - ${{ if ne(parameters.platform.container, '') }}: - container: ${{ parameters.platform.container }} - - ${{ if eq(parameters.platform.pool, '') }}: - # The default VM host AzDO pool. This should be capable of running Docker containers: almost all - # source-build builds run in Docker, including the default managed platform. - # /eng/common/templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 - - ${{ if ne(parameters.platform.pool, '') }}: - pool: ${{ parameters.platform.pool }} - - workspace: - clean: all +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: false - steps: - - template: /eng/common/templates/steps/source-build.yml - parameters: - platform: ${{ parameters.platform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b98202aa02d82..89f3291593cb7 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -1,67 +1,7 @@ -parameters: - runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 - sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json - sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" - preSteps: [] - binlogPath: artifacts/log/Debug/Build.binlog - condition: '' - dependsOn: '' - pool: '' - jobs: -- job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} - variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} - - name: SourceIndexPackageSource - value: ${{ parameters.sourceIndexPackageSource }} - - name: BinlogPath - value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $(DncEngPublicBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64.open - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 - inputs: - packageType: sdk - version: 6.0.x - installationPath: $(Agent.TempDirectory)/dotnet - workingDirectory: $(Agent.TempDirectory) - - - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - displayName: Download Tools - # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. - workingDirectory: $(Agent.TempDirectory) - - - script: ${{ parameters.sourceIndexBuildCommand }} - displayName: Build Repository - - - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output - displayName: Process Binlog into indexable sln +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: false - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) - displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/codeql-build.yml b/eng/common/templates/jobs/codeql-build.yml index f7dc5ea4aaa63..517f24d6a52ce 100644 --- a/eng/common/templates/jobs/codeql-build.yml +++ b/eng/common/templates/jobs/codeql-build.yml @@ -1,31 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - jobs: -- template: /eng/common/templates/jobs/jobs.yml +- template: /eng/common/core-templates/jobs/codeql-build.yml parameters: - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishTestResults: false - enablePublishBuildAssets: false - enablePublishUsingPipelines: false - enableTelemetry: true + is1ESPipeline: false - variables: - - group: Publish-Build-Assets - # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in - # sync with the packages.config file. - - name: DefaultGuardianVersion - value: 0.109.0 - - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - jobs: ${{ parameters.jobs }} - + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 289bb2396ce83..388e9037b3e60 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -1,97 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: Enable publishing using release pipelines - enablePublishUsingPipelines: false - - # Optional: Enable running the source-build jobs to build repo from source - enableSourceBuild: false - - # Optional: Parameters for source-build template. - # See /eng/common/templates/jobs/source-build.yml for options - sourceBuildParameters: [] - - graphFileGeneration: - # Optional: Enable generating the graph files at the end of the build - enabled: false - # Optional: Include toolset dependencies in the generated graph files - includeToolset: false - - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - - # Optional: Override automatically derived dependsOn value for "publish build assets" job - publishBuildAssetsDependsOn: '' - - # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. - publishAssetsImmediately: false - - # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) - artifactsPublishingAdditionalParameters: '' - signingValidationAdditionalParameters: '' - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - enableSourceIndex: false - sourceIndexParams: {} - -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - jobs: -- ${{ each job in parameters.jobs }}: - - template: ../job/job.yml - parameters: - # pass along parameters - ${{ each parameter in parameters }}: - ${{ if ne(parameter.key, 'jobs') }}: - ${{ parameter.key }}: ${{ parameter.value }} - - # pass along job properties - ${{ each property in job }}: - ${{ if ne(property.key, 'job') }}: - ${{ property.key }}: ${{ property.value }} - - name: ${{ job.job }} - -- ${{ if eq(parameters.enableSourceBuild, true) }}: - - template: /eng/common/templates/jobs/source-build.yml - parameters: - allCompletedJobId: Source_Build_Complete - ${{ each parameter in parameters.sourceBuildParameters }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if eq(parameters.enableSourceIndex, 'true') }}: - - template: ../job/source-index-stage1.yml - parameters: - runAsPublic: ${{ parameters.runAsPublic }} - ${{ each parameter in parameters.sourceIndexParams }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: - - template: ../job/publish-build-assets.yml - parameters: - continueOnError: ${{ parameters.continueOnError }} - dependsOn: - - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.publishBuildAssetsDependsOn }}: - - ${{ job.job }} - - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.jobs }}: - - ${{ job.job }} - - ${{ if eq(parameters.enableSourceBuild, true) }}: - - Source_Build_Complete +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: false - runAsPublic: ${{ parameters.runAsPublic }} - publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} - publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} - enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/source-build.yml b/eng/common/templates/jobs/source-build.yml index a15b07eb51d9d..818d4c326dbbf 100644 --- a/eng/common/templates/jobs/source-build.yml +++ b/eng/common/templates/jobs/source-build.yml @@ -1,46 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. A job is created for each platform, as - # well as an optional server job that completes when all platform jobs complete. - - # The name of the "join" job for all source-build platforms. If set to empty string, the job is - # not included. Existing repo pipelines can use this job depend on all source-build jobs - # completing without maintaining a separate list of every single job ID: just depend on this one - # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. - allCompletedJobId: '' - - # See /eng/common/templates/job/source-build.yml - jobNamePrefix: 'Source_Build' - - # This is the default platform provided by Arcade, intended for use by a managed-only repo. - defaultManagedPlatform: - name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' - - # Defines the platforms on which to run build jobs. One job is created for each platform, and the - # object in this array is sent to the job template as 'platform'. If no platforms are specified, - # one job runs on 'defaultManagedPlatform'. - platforms: [] - jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: false -- ${{ if ne(parameters.allCompletedJobId, '') }}: - - job: ${{ parameters.allCompletedJobId }} - displayName: Source-Build Complete - pool: server - dependsOn: - - ${{ each platform in parameters.platforms }}: - - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(length(parameters.platforms), 0) }}: - - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - -- ${{ each platform in parameters.platforms }}: - - template: /eng/common/templates/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ platform }} - -- ${{ if eq(length(parameters.platforms), 0) }}: - - template: /eng/common/templates/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ parameters.defaultManagedPlatform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index 173914f2364a7..7fa105875592c 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -1,22 +1,8 @@ variables: - - group: Publish-Build-Assets +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - # Whether the build is internal or not - - name: IsInternalBuild - value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} - - # Default Maestro++ API Endpoint and API Version - - name: MaestroApiEndPoint - value: "https://maestro.dot.net" - - name: MaestroApiAccessToken - value: $(MaestroAccessToken) - - name: MaestroApiVersion - value: "2020-02-20" - - - name: SourceLinkCLIVersion - value: 3.0.0 - - name: SymbolToolVersion - value: 1.0.1 - - - name: runCodesignValidationInjection - value: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index aba44a25a3387..53ede714bdd20 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -1,281 +1,8 @@ -parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - stages: -- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - - stage: Validate - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Validate Build Assets - variables: - - template: common-variables.yml - - template: /eng/common/templates/variables/pool-providers.yml - jobs: - - job: - displayName: NuGet Validation - condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ - - - job: - displayName: Signing Validation - condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - itemPattern: | - ** - !**/Microsoft.SourceBuild.Intermediate.*.nupkg - - # This is necessary whenever we want to publish/restore to an AzDO private feed - # Since sdk-task.ps1 tries to restore packages we need to do this authentication here - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: ../steps/publish-logs.yml - parameters: - StageLabel: 'Validation' - JobLabel: 'Signing' - - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true - - - template: /eng/common/templates/job/execute-sdl.yml - parameters: - enable: ${{ parameters.SDLValidationParameters.enable }} - publishGuardianDirectoryToPipeline: ${{ parameters.SDLValidationParameters.publishGdn }} - additionalParameters: ${{ parameters.SDLValidationParameters.params }} - continueOnError: ${{ parameters.SDLValidationParameters.continueOnError }} - artifactNames: ${{ parameters.SDLValidationParameters.artifactNames }} - downloadArtifacts: ${{ parameters.SDLValidationParameters.downloadArtifacts }} - -- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - - stage: publish_using_darc - ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - dependsOn: ${{ parameters.publishDependsOn }} - ${{ else }}: - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Publish using Darc - variables: - - template: common-variables.yml - - template: /eng/common/templates/variables/pool-providers.yml - jobs: - - job: - displayName: Publish Using Darc - timeoutInMinutes: 120 - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: NuGetAuthenticate@1 +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml index 0c87f149a4ad7..a79fab5b441e8 100644 --- a/eng/common/templates/post-build/setup-maestro-vars.yml +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -1,70 +1,8 @@ -parameters: - BARBuildId: '' - PromoteToChannelIds: '' - steps: - - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Release Configs - inputs: - buildType: current - artifactName: ReleaseConfigs - checkDownloadedFiles: true - - - task: PowerShell@2 - name: setReleaseVars - displayName: Set Release Configs Vars - inputs: - targetType: inline - pwsh: true - script: | - try { - if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { - $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt - - $BarId = $Content | Select -Index 0 - $Channels = $Content | Select -Index 1 - $IsStableBuild = $Content | Select -Index 2 - - $AzureDevOpsProject = $Env:System_TeamProject - $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId - $AzureDevOpsBuildId = $Env:Build_BuildId - } - else { - $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" - - $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $apiHeaders.Add('Accept', 'application/json') - $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") - - $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } - - $BarId = $Env:BARBuildId - $Channels = $Env:PromoteToMaestroChannels -split "," - $Channels = $Channels -join "][" - $Channels = "[$Channels]" - - $IsStableBuild = $buildInfo.stable - $AzureDevOpsProject = $buildInfo.azureDevOpsProject - $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId - $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId - } - - Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" - Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" - Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" - Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" - Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" - } - catch { - Write-Host $_ - Write-Host $_.Exception - Write-Host $_.ScriptStackTrace - exit 1 - } - env: - MAESTRO_API_TOKEN: $(MaestroApiAccessToken) - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/steps/add-build-to-channel.yml b/eng/common/templates/steps/add-build-to-channel.yml index f67a210d62f3e..42bbba161b9b6 100644 --- a/eng/common/templates/steps/add-build-to-channel.yml +++ b/eng/common/templates/steps/add-build-to-channel.yml @@ -1,13 +1,7 @@ -parameters: - ChannelId: 0 - steps: -- task: PowerShell@2 - displayName: Add Build to Channel - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 - arguments: -BuildId $(BARBuildId) - -ChannelId ${{ parameters.ChannelId }} - -MaestroApiAccessToken $(MaestroApiAccessToken) - -MaestroApiEndPoint $(MaestroApiEndPoint) - -MaestroApiVersion $(MaestroApiVersion) +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/build-reason.yml b/eng/common/templates/steps/build-reason.yml deleted file mode 100644 index eba58109b52c9..0000000000000 --- a/eng/common/templates/steps/build-reason.yml +++ /dev/null @@ -1,12 +0,0 @@ -# build-reason.yml -# Description: runs steps if build.reason condition is valid. conditions is a string of valid build reasons -# to include steps (',' separated). -parameters: - conditions: '' - steps: [] - -steps: - - ${{ if and( not(startsWith(parameters.conditions, 'not')), contains(parameters.conditions, variables['build.reason'])) }}: - - ${{ parameters.steps }} - - ${{ if and( startsWith(parameters.conditions, 'not'), not(contains(parameters.conditions, variables['build.reason']))) }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml index cbba0596709da..c12a5f8d21d76 100644 --- a/eng/common/templates/steps/component-governance.yml +++ b/eng/common/templates/steps/component-governance.yml @@ -1,13 +1,7 @@ -parameters: - disableComponentGovernance: false - componentGovernanceIgnoreDirectories: '' - steps: -- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: - - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" - displayName: Set skipComponentGovernanceDetection variable -- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true - inputs: - ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/execute-codeql.yml b/eng/common/templates/steps/execute-codeql.yml deleted file mode 100644 index 3930b1630214b..0000000000000 --- a/eng/common/templates/steps/execute-codeql.yml +++ /dev/null @@ -1,32 +0,0 @@ -parameters: - # Language that should be analyzed. Defaults to csharp - language: csharp - # Build Commands - buildCommands: '' - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - # optional: determines whether to continue the build if the step errors; - sdlContinueOnError: false - -steps: -- template: /eng/common/templates/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: '${{ parameters.additionalParameters }} - -CodeQLAdditionalRunConfigParams @("BuildCommands < ${{ parameters.buildCommands }}", "Language < ${{ parameters.language }}")' - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} \ No newline at end of file diff --git a/eng/common/templates/steps/execute-sdl.yml b/eng/common/templates/steps/execute-sdl.yml deleted file mode 100644 index 07426fde05d82..0000000000000 --- a/eng/common/templates/steps/execute-sdl.yml +++ /dev/null @@ -1,88 +0,0 @@ -parameters: - overrideGuardianVersion: '' - executeAllSdlToolsScript: '' - overrideParameters: '' - additionalParameters: '' - publishGuardianDirectoryToPipeline: false - sdlContinueOnError: false - condition: '' - -steps: -- task: NuGetAuthenticate@1 - inputs: - nuGetServiceConnections: GuardianConnect - -- task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - -- ${{ if ne(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts -Version ${{ parameters.overrideGuardianVersion }} - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian (Overridden) - -- ${{ if eq(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian - -- ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} - displayName: Execute SDL (Overridden) - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} - -GuardianCliLocation $(GuardianCliLocation) - -NugetPackageDirectory $(Build.SourcesDirectory)\.packages - -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) - ${{ parameters.additionalParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: - # We want to publish the Guardian results and configuration for easy diagnosis. However, the - # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default - # tooling files. Some of these files are large and aren't useful during an investigation, so - # exclude them by simply deleting them before publishing. (As of writing, there is no documented - # way to selectively exclude a dir from the pipeline artifact publish task.) - - task: DeleteFiles@1 - displayName: Delete Guardian dependencies to avoid uploading - inputs: - SourceFolder: $(Agent.BuildDirectory)/.gdn - Contents: | - c - i - condition: succeededOrFailed() - - - publish: $(Agent.BuildDirectory)/.gdn - artifact: GuardianConfiguration - displayName: Publish GuardianConfiguration - condition: succeededOrFailed() - - # Publish the SARIF files in a container named CodeAnalysisLogs to enable integration - # with the "SARIF SAST Scans Tab" Azure DevOps extension - - task: CopyFiles@2 - displayName: Copy SARIF files - inputs: - flattenFolders: true - sourceFolder: $(Agent.BuildDirectory)/.gdn/rc/ - contents: '**/*.sarif' - targetFolder: $(Build.SourcesDirectory)/CodeAnalysisLogs - condition: succeededOrFailed() - - # Use PublishBuildArtifacts because the SARIF extension only checks this case - # see microsoft/sarif-azuredevops-extension#4 - - task: PublishBuildArtifacts@1 - displayName: Publish SARIF files to CodeAnalysisLogs container - inputs: - pathToPublish: $(Build.SourcesDirectory)/CodeAnalysisLogs - artifactName: CodeAnalysisLogs - condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml index 2b21eae427328..26dc00a2e0f31 100644 --- a/eng/common/templates/steps/generate-sbom.yml +++ b/eng/common/templates/steps/generate-sbom.yml @@ -1,48 +1,7 @@ -# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. -# PackageName - The name of the package this SBOM represents. -# PackageVersion - The version of the package this SBOM represents. -# ManifestDirPath - The path of the directory where the generated manifest files will be placed -# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. - -parameters: - PackageVersion: 8.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - PackageName: '.NET' - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - IgnoreDirectories: '' - sbomContinueOnError: true - steps: -- task: PowerShell@2 - displayName: Prep for SBOM generation in (Non-linux) - condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) - inputs: - filePath: ./eng/common/generate-sbom-prep.ps1 - arguments: ${{parameters.manifestDirPath}} - -# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 -- script: | - chmod +x ./eng/common/generate-sbom-prep.sh - ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} - displayName: Prep for SBOM generation in (Linux) - condition: eq(variables['Agent.Os'], 'Linux') - continueOnError: ${{ parameters.sbomContinueOnError }} - -- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: 'Generate SBOM manifest' - continueOnError: ${{ parameters.sbomContinueOnError }} - inputs: - PackageName: ${{ parameters.packageName }} - BuildDropPath: ${{ parameters.buildDropPath }} - PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }} - ${{ if ne(parameters.IgnoreDirectories, '') }}: - AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' - -- task: PublishPipelineArtifact@1 - displayName: Publish SBOM manifest - continueOnError: ${{parameters.sbomContinueOnError}} - inputs: - targetPath: '${{parameters.manifestDirPath}}' - artifactName: $(ARTIFACT_NAME) +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-build-artifacts.yml b/eng/common/templates/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..6428a98dfef68 --- /dev/null +++ b/eng/common/templates/steps/publish-build-artifacts.yml @@ -0,0 +1,40 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} \ No newline at end of file diff --git a/eng/common/templates/steps/publish-logs.yml b/eng/common/templates/steps/publish-logs.yml index 88f238f36bfd8..4ea86bd882355 100644 --- a/eng/common/templates/steps/publish-logs.yml +++ b/eng/common/templates/steps/publish-logs.yml @@ -1,23 +1,7 @@ -parameters: - StageLabel: '' - JobLabel: '' - steps: -- task: Powershell@2 - displayName: Prepare Binlogs to Upload - inputs: - targetType: inline - script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - continueOnError: true - condition: always() +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: false -- task: PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/PostBuildLogs' - PublishLocation: Container - ArtifactName: PostBuildLogs - continueOnError: true - condition: always() + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-pipeline-artifacts.yml b/eng/common/templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..5dd698b212fc6 --- /dev/null +++ b/eng/common/templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,34 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.publishLocation }}: + publishLocation: ${{ parameters.args.publishLocation }} + ${{ if parameters.args.fileSharePath }}: + fileSharePath: ${{ parameters.args.fileSharePath }} + ${{ if parameters.args.Parallel }}: + parallel: ${{ parameters.args.Parallel }} + ${{ if parameters.args.parallelCount }}: + parallelCount: ${{ parameters.args.parallelCount }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates/steps/retain-build.yml b/eng/common/templates/steps/retain-build.yml index 83d97a26a01ff..8e841ace3d293 100644 --- a/eng/common/templates/steps/retain-build.yml +++ b/eng/common/templates/steps/retain-build.yml @@ -1,28 +1,7 @@ -parameters: - # Optional azure devops PAT with build execute permissions for the build's organization, - # only needed if the build that should be retained ran on a different organization than - # the pipeline where this template is executing from - Token: '' - # Optional BuildId to retain, defaults to the current running build - BuildId: '' - # Azure devops Organization URI for the build in the https://dev.azure.com/ format. - # Defaults to the organization the current pipeline is running on - AzdoOrgUri: '$(System.CollectionUri)' - # Azure devops project for the build. Defaults to the project the current pipeline is running on - AzdoProject: '$(System.TeamProject)' - steps: - - task: powershell@2 - inputs: - targetType: 'filePath' - filePath: eng/common/retain-build.ps1 - pwsh: true - arguments: > - -AzdoOrgUri: ${{parameters.AzdoOrgUri}} - -AzdoProject ${{parameters.AzdoProject}} - -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} - -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} - displayName: Enable permanent build retention - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - BUILD_ID: $(Build.BuildId) \ No newline at end of file +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/run-on-unix.yml b/eng/common/templates/steps/run-on-unix.yml deleted file mode 100644 index e1733814f65dc..0000000000000 --- a/eng/common/templates/steps/run-on-unix.yml +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - agentOs: '' - steps: [] - -steps: -- ${{ if ne(parameters.agentOs, 'Windows_NT') }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/run-on-windows.yml b/eng/common/templates/steps/run-on-windows.yml deleted file mode 100644 index 73e7e9c275a1f..0000000000000 --- a/eng/common/templates/steps/run-on-windows.yml +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - agentOs: '' - steps: [] - -steps: -- ${{ if eq(parameters.agentOs, 'Windows_NT') }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/run-script-ifequalelse.yml b/eng/common/templates/steps/run-script-ifequalelse.yml deleted file mode 100644 index 3d1242f5587c8..0000000000000 --- a/eng/common/templates/steps/run-script-ifequalelse.yml +++ /dev/null @@ -1,33 +0,0 @@ -parameters: - # if parameter1 equals parameter 2, run 'ifScript' command, else run 'elsescript' command - parameter1: '' - parameter2: '' - ifScript: '' - elseScript: '' - - # name of script step - name: Script - - # display name of script step - displayName: If-Equal-Else Script - - # environment - env: {} - - # conditional expression for step execution - condition: '' - -steps: -- ${{ if and(ne(parameters.ifScript, ''), eq(parameters.parameter1, parameters.parameter2)) }}: - - script: ${{ parameters.ifScript }} - name: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - env: ${{ parameters.env }} - condition: ${{ parameters.condition }} - -- ${{ if and(ne(parameters.elseScript, ''), ne(parameters.parameter1, parameters.parameter2)) }}: - - script: ${{ parameters.elseScript }} - name: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - env: ${{ parameters.env }} - condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index 3eb7e2d5f840c..39f99fc2762d0 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -1,91 +1,7 @@ -# Please remember to update the documentation if you make changes to these parameters! -parameters: - HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' - HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number - HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues - HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group - HelixConfiguration: '' # optional -- additional property attached to a job - HelixPreCommands: '' # optional -- commands to run before Helix work item execution - HelixPostCommands: '' # optional -- commands to run after Helix work item execution - WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects - WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects - WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects - CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload - XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true - XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects - XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects - XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner - XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects - IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." - IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set - HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) - Creator: '' # optional -- if the build is external, use this to specify who is sending the job - DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO - condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() - continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false - steps: - - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' - displayName: ${{ parameters.DisplayNamePrefix }} (Windows) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog - displayName: ${{ parameters.DisplayNamePrefix }} (Unix) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index 41bbb915736a6..23c1d6f4e9f8d 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -1,129 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. - - # This is a 'steps' template, and is intended for advanced scenarios where the existing build - # infra has a careful build methodology that must be followed. For example, a repo - # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline - # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to - # GitHub. Using this steps template leaves room for that infra to be included. - - # Defines the platform on which to run the steps. See 'eng/common/templates/job/source-build.yml' - # for details. The entire object is described in the 'job' template for simplicity, even though - # the usage of the properties on this object is split between the 'job' and 'steps' templates. - platform: {} - steps: -# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) -- script: | - set -x - df -h - - # If building on the internal project, the artifact feeds variable may be available (usually only if needed) - # In that case, call the feed setup script to add internal feeds corresponding to public ones. - # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. - # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those - # changes. - internalRestoreArgs= - if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then - # Temporarily work around https://github.com/dotnet/arcade/issues/7709 - chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) - internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. - # This only works if there is a username/email configured, which won't be the case in most CI runs. - git config --get user.email - if [ $? -ne 0 ]; then - git config user.email dn-bot@microsoft.com - git config user.name dn-bot - fi - fi - - # If building on the internal project, the internal storage variable may be available (usually only if needed) - # In that case, add variables to allow the download of internal runtimes if the specified versions are not found - # in the default public locations. - internalRuntimeDownloadArgs= - if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' - fi - - buildConfig=Release - # Check if AzDO substitutes in a build config from a variable, and use it if so. - if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then - buildConfig='$(_BuildConfig)' - fi - - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - - targetRidArgs= - if [ '${{ parameters.platform.targetRID }}' != '' ]; then - targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' - fi - - runtimeOsArgs= - if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then - runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' - fi - - baseOsArgs= - if [ '${{ parameters.platform.baseOS }}' != '' ]; then - baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' - fi - - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - - ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ - --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ - $officialBuildArgs \ - $internalRuntimeDownloadArgs \ - $internalRestoreArgs \ - $targetRidArgs \ - $runtimeOsArgs \ - $baseOsArgs \ - /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ - /p:ArcadeBuildFromSource=true \ - /p:AssetManifestFileName=$assetManifestFileName - displayName: Build - -# Upload build logs for diagnosis. -- task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(Build.SourcesDirectory)' - Contents: | - **/*.log - **/*.binlog - artifacts/source-build/self/prebuilt-report/** - TargetFolder: '$(Build.StagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - -- task: PublishPipelineArtifact@1 - displayName: Publish BuildLogs - inputs: - targetPath: '$(Build.StagingDirectory)/BuildLogs' - artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) - continueOnError: true - condition: succeededOrFailed() +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: false -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- task: ComponentGovernanceComponentDetection@0 - displayName: Component Detection (Exclude upstream cache) - inputs: - ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/telemetry-end.yml b/eng/common/templates/steps/telemetry-end.yml deleted file mode 100644 index fadc04ca1b9a3..0000000000000 --- a/eng/common/templates/steps/telemetry-end.yml +++ /dev/null @@ -1,102 +0,0 @@ -parameters: - maxRetries: 5 - retryDelay: 10 # in seconds - -steps: -- bash: | - if [ "$AGENT_JOBSTATUS" = "Succeeded" ] || [ "$AGENT_JOBSTATUS" = "PartiallySucceeded" ]; then - errorCount=0 - else - errorCount=1 - fi - warningCount=0 - - curlStatus=1 - retryCount=0 - # retry loop to harden against spotty telemetry connections - # we don't retry successes and 4xx client errors - until [[ $curlStatus -eq 0 || ( $curlStatus -ge 400 && $curlStatus -le 499 ) || $retryCount -ge $MaxRetries ]] - do - if [ $retryCount -gt 0 ]; then - echo "Failed to send telemetry to Helix; waiting $RetryDelay seconds before retrying..." - sleep $RetryDelay - fi - - # create a temporary file for curl output - res=`mktemp` - - curlResult=` - curl --verbose --output $res --write-out "%{http_code}"\ - -H 'Content-Type: application/json' \ - -H "X-Helix-Job-Token: $Helix_JobToken" \ - -H 'Content-Length: 0' \ - -X POST -G "https://helix.dot.net/api/2018-03-14/telemetry/job/build/$Helix_WorkItemId/finish" \ - --data-urlencode "errorCount=$errorCount" \ - --data-urlencode "warningCount=$warningCount"` - curlStatus=$? - - if [ $curlStatus -eq 0 ]; then - if [ $curlResult -gt 299 ] || [ $curlResult -lt 200 ]; then - curlStatus=$curlResult - fi - fi - - let retryCount++ - done - - if [ $curlStatus -ne 0 ]; then - echo "Failed to Send Build Finish information after $retryCount retries" - vstsLogOutput="vso[task.logissue type=error;sourcepath=templates/steps/telemetry-end.yml;code=1;]Failed to Send Build Finish information: $curlStatus" - echo "##$vstsLogOutput" - exit 1 - fi - displayName: Send Unix Build End Telemetry - env: - # defined via VSTS variables in start-job.sh - Helix_JobToken: $(Helix_JobToken) - Helix_WorkItemId: $(Helix_WorkItemId) - MaxRetries: ${{ parameters.maxRetries }} - RetryDelay: ${{ parameters.retryDelay }} - condition: and(always(), ne(variables['Agent.Os'], 'Windows_NT')) -- powershell: | - if (($env:Agent_JobStatus -eq 'Succeeded') -or ($env:Agent_JobStatus -eq 'PartiallySucceeded')) { - $ErrorCount = 0 - } else { - $ErrorCount = 1 - } - $WarningCount = 0 - - # Basic retry loop to harden against server flakiness - $retryCount = 0 - while ($retryCount -lt $env:MaxRetries) { - try { - Invoke-RestMethod -Uri "https://helix.dot.net/api/2018-03-14/telemetry/job/build/$env:Helix_WorkItemId/finish?errorCount=$ErrorCount&warningCount=$WarningCount" -Method Post -ContentType "application/json" -Body "" ` - -Headers @{ 'X-Helix-Job-Token'=$env:Helix_JobToken } - break - } - catch { - $statusCode = $_.Exception.Response.StatusCode.value__ - if ($statusCode -ge 400 -and $statusCode -le 499) { - Write-Host "##vso[task.logissue]error Failed to send telemetry to Helix (status code $statusCode); not retrying (4xx client error)" - Write-Host "##vso[task.logissue]error ", $_.Exception.GetType().FullName, $_.Exception.Message - exit 1 - } - Write-Host "Failed to send telemetry to Helix (status code $statusCode); waiting $env:RetryDelay seconds before retrying..." - $retryCount++ - sleep $env:RetryDelay - continue - } - } - - if ($retryCount -ge $env:MaxRetries) { - Write-Host "##vso[task.logissue]error Failed to send telemetry to Helix after $retryCount retries." - exit 1 - } - displayName: Send Windows Build End Telemetry - env: - # defined via VSTS variables in start-job.ps1 - Helix_JobToken: $(Helix_JobToken) - Helix_WorkItemId: $(Helix_WorkItemId) - MaxRetries: ${{ parameters.maxRetries }} - RetryDelay: ${{ parameters.retryDelay }} - condition: and(always(),eq(variables['Agent.Os'], 'Windows_NT')) diff --git a/eng/common/templates/steps/telemetry-start.yml b/eng/common/templates/steps/telemetry-start.yml deleted file mode 100644 index 32c01ef0b553b..0000000000000 --- a/eng/common/templates/steps/telemetry-start.yml +++ /dev/null @@ -1,241 +0,0 @@ -parameters: - helixSource: 'undefined_defaulted_in_telemetry.yml' - helixType: 'undefined_defaulted_in_telemetry.yml' - buildConfig: '' - runAsPublic: false - maxRetries: 5 - retryDelay: 10 # in seconds - -steps: -- ${{ if and(eq(parameters.runAsPublic, 'false'), not(eq(variables['System.TeamProject'], 'public'))) }}: - - task: AzureKeyVault@1 - inputs: - azureSubscription: 'HelixProd_KeyVault' - KeyVaultName: HelixProdKV - SecretsFilter: 'HelixApiAccessToken' - condition: always() -- bash: | - # create a temporary file - jobInfo=`mktemp` - - # write job info content to temporary file - cat > $jobInfo < /dev/null; then echo "Curl failed; dumping some information about dotnet.microsoft.com for later investigation" - echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 + echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 || true fi echo "Will now retry the same URL with verbose logging." with_retries curl "$install_script_url" -sSL --verbose --retry 10 --create-dirs -o "$install_script" || { @@ -343,7 +341,7 @@ function InitializeBuildTool { _InitializeBuildToolCommand="msbuild" # use override if it exists - commonly set by source-build if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then - _InitializeBuildToolFramework="net8.0" + _InitializeBuildToolFramework="net9.0" else _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" fi @@ -458,12 +456,10 @@ function MSBuild { local possiblePaths=() possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.Arcade.Sdk.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.Arcade.Sdk.dll" ) for path in "${possiblePaths[@]}"; do if [[ -f $path ]]; then selectedPath=$path @@ -510,7 +506,8 @@ function MSBuild-Core { echo "Build failed with exit code $exit_code. Check errors above." # When running on Azure Pipelines, override the returned exit code to avoid double logging. - if [[ "$ci" == "true" && -n ${SYSTEM_TEAMPROJECT:-} ]]; then + # Skip this when the build is a child of the VMR orchestrator build. + if [[ "$ci" == true && -n ${SYSTEM_TEAMPROJECT:-} && "$product_build" != true && "$properties" != *"DotNetBuildRepo=true"* ]]; then Write-PipelineSetResult -result "Failed" -message "msbuild execution failed." # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index 99698db200629..3598b4bdc7671 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -58,6 +58,8 @@ false true + + <_SkipUpgradeNetAnalyzersNuGetWarning>true diff --git a/eng/targets/TargetFrameworks.props b/eng/targets/TargetFrameworks.props index 20b3ce78a455f..8493c1046cb51 100644 --- a/eng/targets/TargetFrameworks.props +++ b/eng/targets/TargetFrameworks.props @@ -18,6 +18,7 @@ net8.0 net7.0;net8.0 net6.0 + net9.0 + + $(NetMinimum) $(NetPrevious) $(NetCurrent);$(NetPrevious) $(NetCurrent);$(NetPrevious) $(NetPrevious) + $(NetPrevious) @@ -52,6 +56,7 @@ $(NetCurrent);$(NetPrevious) $(NetCurrent);$(NetPrevious) $(NetCurrent) + $(NetCurrent) diff --git a/global.json b/global.json index 3371f1e96e480..1ad426b264b05 100644 --- a/global.json +++ b/global.json @@ -1,19 +1,19 @@ { "sdk": { - "version": "8.0.101", + "version": "9.0.100-preview.3.24204.13", "allowPrerelease": false, "rollForward": "patch" }, "tools": { - "dotnet": "8.0.101", + "dotnet": "9.0.100-preview.3.24204.13", "vs": { "version": "17.8.0" }, "xcopy-msbuild": "17.8.1-2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24204.3", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24204.3", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24265.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24265.1", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzer.cs index 6fd5116709f92..8bc17b8207a5b 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzer.cs @@ -130,10 +130,10 @@ private static bool IsTopmostExpression(ExpressionSyntax node) { return node.WalkUpParentheses().Parent switch { - LambdaExpressionSyntax _ => true, - AssignmentExpressionSyntax _ => true, - ConditionalExpressionSyntax _ => true, - ExpressionSyntax _ => false, + LambdaExpressionSyntax => true, + AssignmentExpressionSyntax => true, + ConditionalExpressionSyntax => true, + ExpressionSyntax => false, _ => true }; } @@ -142,10 +142,10 @@ private static bool IsTrivial(AnalyzedPattern pattern) { return pattern switch { - Not { Pattern: Constant _ } => true, - Not { Pattern: Source { PatternSyntax: ConstantPatternSyntax _ } } => true, - Not _ => false, - Binary _ => false, + Not { Pattern: Constant } => true, + Not { Pattern: Source { PatternSyntax: ConstantPatternSyntax } } => true, + Not => false, + Binary => false, _ => true }; } diff --git a/src/Analyzers/CSharp/CodeFixes/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs index b23010c17f6a6..c24bdc78fec87 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs @@ -28,6 +28,8 @@ internal class CSharpMakeMethodAsynchronousCodeFixProvider : AbstractMakeMethodA private const string CS4034 = nameof(CS4034); // The 'await' operator can only be used within an async lambda expression. Consider marking this method with the 'async' modifier. private const string CS0246 = nameof(CS0246); // The type or namespace name 'await' could not be found + private static readonly SyntaxToken s_asyncKeywordWithSpace = AsyncKeyword.WithoutTrivia().WithTrailingTrivia(Space); + [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] public CSharpMakeMethodAsynchronousCodeFixProvider() @@ -92,7 +94,7 @@ private static MethodDeclarationSyntax FixMethod( CancellationToken cancellationToken) { var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, method.ReturnType, knownTypes, cancellationToken); - var newModifiers = AddAsyncModifierWithCorrectedTrivia(method.Modifiers, ref newReturnType); + (var newModifiers, newReturnType) = AddAsyncModifierWithCorrectedTrivia(method.Modifiers, newReturnType); return method.WithReturnType(newReturnType).WithModifiers(newModifiers); } @@ -104,7 +106,7 @@ private static LocalFunctionStatementSyntax FixLocalFunction( CancellationToken cancellationToken) { var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, localFunction.ReturnType, knownTypes, cancellationToken); - var newModifiers = AddAsyncModifierWithCorrectedTrivia(localFunction.Modifiers, ref newReturnType); + (var newModifiers, newReturnType) = AddAsyncModifierWithCorrectedTrivia(localFunction.Modifiers, newReturnType); return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers); } @@ -176,15 +178,15 @@ private static bool IsIEnumerable(ITypeSymbol returnType, KnownTaskTypes knownTy private static bool IsIEnumerator(ITypeSymbol returnType, KnownTaskTypes knownTypes) => returnType.OriginalDefinition.Equals(knownTypes.IEnumeratorOfTType); - private static SyntaxTokenList AddAsyncModifierWithCorrectedTrivia(SyntaxTokenList modifiers, ref TypeSyntax newReturnType) + private static (SyntaxTokenList newModifiers, TypeSyntax newReturnType) AddAsyncModifierWithCorrectedTrivia(SyntaxTokenList modifiers, TypeSyntax returnType) { if (modifiers.Any()) - return modifiers.Add(AsyncKeyword); + return (modifiers.Add(s_asyncKeywordWithSpace), returnType); // Move the leading trivia from the return type to the new modifiers list. - var result = TokenList(AsyncKeyword.WithLeadingTrivia(newReturnType.GetLeadingTrivia())); - newReturnType = newReturnType.WithoutLeadingTrivia(); - return result; + var newModifiers = TokenList(s_asyncKeywordWithSpace.WithLeadingTrivia(returnType.GetLeadingTrivia())); + var newReturnType = returnType.WithoutLeadingTrivia(); + return (newModifiers, newReturnType); } private static AnonymousFunctionExpressionSyntax FixAnonymousFunction(AnonymousFunctionExpressionSyntax anonymous) diff --git a/src/Analyzers/CSharp/Tests/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests_FixAllTests.cs b/src/Analyzers/CSharp/Tests/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests_FixAllTests.cs index 5ccf06df80da4..d3d1a1daad986 100644 --- a/src/Analyzers/CSharp/Tests/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests_FixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests_FixAllTests.cs @@ -46,7 +46,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } @@ -86,7 +85,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } @@ -126,7 +124,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } @@ -166,7 +163,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } @@ -206,7 +202,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } @@ -246,7 +241,6 @@ class C void Goo() { a?.Invoke(); - a?.Invoke(); } } diff --git a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs index d059a959a662b..47bfbcf7b3668 100644 --- a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs @@ -902,7 +902,8 @@ class C { public void M1() { - Func foo = x => { + Func foo = x => + { if (System.DateTime.Now.Ticks > 0) { return Task.CompletedTask; @@ -1040,7 +1041,8 @@ class C { public void M1() { - Func foo = () => { + Func foo = () => + { if (System.DateTime.Now.Ticks > 0) { return Task.CompletedTask; diff --git a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs index 6a8bc64328d97..1cfa1624abc17 100644 --- a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs @@ -49,7 +49,7 @@ class C { void M(int? x, int? y) { - var z = x ?? y ; + var z = x ?? y; } } """); @@ -105,7 +105,7 @@ class C { void M(int? x, int? y) { - var z = (x + y) ?? y ; + var z = (x + y) ?? y; } } """); @@ -163,7 +163,7 @@ class C void M(int? x, int? y) { var z1 = x ?? y; - var z2 = x ?? y ; + var z2 = x ?? y; } } """); @@ -249,7 +249,7 @@ class C { void M(int? x, int? y) { - Expression> e = () => {|Warning:x ?? y|} ; + Expression> e = () => {|Warning:x ?? y|}; } } """); diff --git a/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs b/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs index cc91eb9d9ddc7..ad221cea502b9 100644 --- a/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs +++ b/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; @@ -30,10 +31,10 @@ internal static IEnumerable GetDefaultFormattingRules(IS /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [node.FullSpan], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [node.FullSpan], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); public static SyntaxNode Format(SyntaxNode node, TextSpan spanToFormat, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [spanToFormat], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [spanToFormat], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); /// /// Formats the whitespace of a syntax tree. @@ -44,15 +45,15 @@ public static SyntaxNode Format(SyntaxNode node, TextSpan spanToFormat, ISyntaxF /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) - => Format(node, GetAnnotatedSpans(node, annotation), syntaxFormattingService, options, rules, cancellationToken: cancellationToken); + => Format(node, GetAnnotatedSpans(node, annotation), syntaxFormattingService, options, rules.AsImmutableOrNull(), cancellationToken: cancellationToken); - internal static SyntaxNode Format(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); - internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetTextChanges(cancellationToken); - internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => syntaxFormattingService.GetFormattingResult(node, spans, options, rules, cancellationToken); /// @@ -63,5 +64,5 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerab /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => GetFormattedTextChanges(node, [node.FullSpan], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); } diff --git a/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs b/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs index 572aa92beeb8c..7eb990634811a 100644 --- a/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs +++ b/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs @@ -24,7 +24,7 @@ internal static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context, Format var root = tree.GetRoot(cancellationToken); var span = context.FilterSpan.HasValue ? context.FilterSpan.GetValueOrDefault() : root.FullSpan; var spans = SpecializedCollections.SingletonEnumerable(span); - var formattingChanges = Formatter.GetFormattedTextChanges(root, spans, formattingProvider, options, rules: null, cancellationToken); + var formattingChanges = Formatter.GetFormattedTextChanges(root, spans, formattingProvider, options, rules: default, cancellationToken); // formattingChanges could include changes that impact a larger section of the original document than // necessary. Before reporting diagnostics, process the changes to minimize the span of individual diff --git a/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index cbecadb7094d4..b64d63941b497 100644 --- a/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -177,18 +177,14 @@ private static async Task RemoveAwaitFromCallersAsync( var editor = new SyntaxEditor(root, currentSolution.Services); foreach (var location in group) - { - RemoveAwaitFromCallerIfPresent(editor, syntaxFactsService, root, location, cancellationToken); - } + RemoveAwaitFromCallerIfPresent(editor, syntaxFactsService, location, cancellationToken); var newRoot = editor.GetChangedRoot(); return currentSolution.WithDocumentSyntaxRoot(document.Id, newRoot); } private static void RemoveAwaitFromCallerIfPresent( - SyntaxEditor editor, ISyntaxFactsService syntaxFacts, - SyntaxNode root, ReferenceLocation referenceLocation, - CancellationToken cancellationToken) + SyntaxEditor editor, ISyntaxFactsService syntaxFacts, ReferenceLocation referenceLocation, CancellationToken cancellationToken) { if (referenceLocation.IsImplicit) { diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs index c176e52e9c84b..b70f54298a2fd 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs @@ -850,10 +850,10 @@ private async Task AdjustLocalDeclarationsAsync( // Run formatter prior to invoking IMoveDeclarationNearReferenceService. #if CODE_STYLE var provider = GetSyntaxFormatting(); - rootWithTrackedNodes = FormatterHelper.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); + rootWithTrackedNodes = FormatterHelper.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: default, cancellationToken); #else var provider = document.Project.Solution.Services; - rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); + rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: default, cancellationToken); #endif document = document.WithSyntaxRoot(rootWithTrackedNodes); diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index 5f39393f7b430..93ad981de4bc3 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -72,7 +72,7 @@ await FixOneAsync( // formatted to explicitly format things. Note: all we will format is the new // conditional expression as that's the only node that has the appropriate // annotation on it. - var rules = new List { GetMultiLineFormattingRule() }; + var rules = ImmutableArray.Create(GetMultiLineFormattingRule()); #if CODE_STYLE var provider = GetSyntaxFormatting(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 002b0a25e6cb1..7820bfba27d5f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -872,12 +872,24 @@ private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnos } } - private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) + private void CheckAddressOfInAsyncOrIteratorMethod(SyntaxNode node, BindValueKind valueKind, BindingDiagnosticBag diagnostics) { - if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) + if (valueKind == BindValueKind.AddressOf) { - Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + if (this.IsInAsyncMethod()) + { + Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + } + else if (this.IsDirectlyInIterator && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) + { + Error(diagnostics, ErrorCode.ERR_AddressOfInIterator, node); + } } + } + + private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) + { + CheckAddressOfInAsyncOrIteratorMethod(node, valueKind, diagnostics); // Local constants are never variables. Local variables are sometimes // not to be treated as variables, if they are fixed, declared in a using, @@ -968,10 +980,8 @@ internal partial class Binder private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { Debug.Assert(!RequiresAssignableVariable(BindValueKind.AddressOf)); - if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) - { - Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); - } + + CheckAddressOfInAsyncOrIteratorMethod(node, valueKind, diagnostics); ParameterSymbol parameterSymbol = parameter.ParameterSymbol; diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 24d7b988defbc..5a502ffc14748 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -171,6 +171,7 @@ public override Binder VisitMethodDeclaration(MethodDeclarationSyntax methodDecl } SourceMemberMethodSymbol method = null; + bool isIteratorBody = false; if (usage != NodeUsage.Normal && methodDecl.TypeParameterList != null) { @@ -181,10 +182,11 @@ public override Binder VisitMethodDeclaration(MethodDeclarationSyntax methodDecl if (usage == NodeUsage.MethodBody) { method = method ?? GetMethodSymbol(methodDecl, resultBinder); + isIteratorBody = method.IsIterator; resultBinder = new InMethodBinder(method, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(methodDecl.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(methodDecl.Modifiers, isIteratorBody: isIteratorBody); binderCache.TryAdd(key, resultBinder); } @@ -222,7 +224,7 @@ public override Binder VisitConstructorDeclaration(ConstructorDeclarationSyntax } } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -249,7 +251,7 @@ public override Binder VisitDestructorDeclaration(DestructorDeclarationSyntax pa SourceMemberMethodSymbol method = GetMethodSymbol(parent, resultBinder); resultBinder = new InMethodBinder(method, resultBinder); - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -313,6 +315,10 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent if ((object)accessor != null) { resultBinder = new InMethodBinder(accessor, resultBinder); + + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary( + modifiers: default, + isIteratorBody: accessor.IsIterator); } } @@ -340,12 +346,14 @@ private Binder VisitOperatorOrConversionDeclaration(BaseMethodDeclarationSyntax resultBinder = VisitCore(parent.Parent); MethodSymbol method = GetMethodSymbol(parent, resultBinder); + bool isIteratorBody = false; if ((object)method != null && inBody) { + isIteratorBody = method.IsIterator; resultBinder = new InMethodBinder(method, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers, isIteratorBody: isIteratorBody); binderCache.TryAdd(key, resultBinder); } @@ -365,24 +373,24 @@ public override Binder VisitConversionOperatorDeclaration(ConversionOperatorDecl public override Binder VisitFieldDeclaration(FieldDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitEventDeclaration(EventDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitEventFieldDeclaration(EventFieldDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitPropertyDeclaration(PropertyDeclarationSyntax parent) { if (!LookupPosition.IsInBody(_position, parent)) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } return VisitPropertyOrIndexerExpressionBody(parent); @@ -392,7 +400,7 @@ public override Binder VisitIndexerDeclaration(IndexerDeclarationSyntax parent) { if (!LookupPosition.IsInBody(_position, parent)) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } return VisitPropertyOrIndexerExpressionBody(parent); @@ -405,12 +413,16 @@ private Binder VisitPropertyOrIndexerExpressionBody(BasePropertyDeclarationSynta Binder resultBinder; if (!binderCache.TryGetValue(key, out resultBinder)) { - resultBinder = VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); var propertySymbol = GetPropertySymbol(parent, resultBinder); var accessor = propertySymbol.GetMethod; if ((object)accessor != null) { + // Expression body cannot be an iterator, otherwise we would need to pass + // `isIteratorBody` to `SetOrClearUnsafeRegionIfNecessary` above. + Debug.Assert(!accessor.IsIterator); + resultBinder = new InMethodBinder(accessor, resultBinder); } @@ -672,7 +684,7 @@ public override Binder VisitDelegateDeclaration(DelegateDeclarationSyntax parent resultBinder = new WithClassTypeParametersBinder(container, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -700,7 +712,7 @@ public override Binder VisitEnumDeclaration(EnumDeclarationSyntax parent) resultBinder = new InContainerBinder(container, outer); - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -782,7 +794,7 @@ internal Binder VisitTypeDeclarationCore(TypeDeclarationSyntax parent, NodeUsage } } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 3fb4a0c6d552d..8fadf709dd5e4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3182,21 +3182,13 @@ private BoundExpression BindOutVariableDeclarationArgument( /// /// Reports an error when a bad special by-ref local was found. /// - internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax, ErrorCode errorCode = ErrorCode.ERR_BadSpecialByRefLocal) + internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax) { - Debug.Assert(errorCode is ErrorCode.ERR_BadSpecialByRefLocal or ErrorCode.ERR_BadSpecialByRefUsing or ErrorCode.ERR_BadSpecialByRefLock); if (containingSymbol.Kind == SymbolKind.Method && ((MethodSymbol)containingSymbol).IsAsync && type.IsRestrictedType()) { - if (errorCode == ErrorCode.ERR_BadSpecialByRefLock) - { - Error(diagnostics, errorCode, syntax); - } - else - { - Error(diagnostics, errorCode, syntax, type); - } + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } } @@ -10627,7 +10619,7 @@ bool satisfiesConstraintChecks(MethodSymbol method) parameters.SelectAsArray(p => p.ExplicitDefaultConstantValue) : default; - var hasParams = OverloadResolution.IsValidParams(this, methodSymbol); + var hasParams = OverloadResolution.IsValidParams(this, methodSymbol, out _); Debug.Assert(ContainingMemberOrLambda is { }); Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs index 7374ede9d1163..e594a2999aea2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs @@ -91,11 +91,24 @@ internal Binder WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags flags return new BinderWithContainingMemberOrLambda(this, this.Flags | flags, containing); } - internal Binder WithUnsafeRegionIfNecessary(SyntaxTokenList modifiers) + internal Binder SetOrClearUnsafeRegionIfNecessary(SyntaxTokenList modifiers, bool isIteratorBody = false) { - return (this.Flags.Includes(BinderFlags.UnsafeRegion) || !modifiers.Any(SyntaxKind.UnsafeKeyword)) - ? this - : new Binder(this, this.Flags | BinderFlags.UnsafeRegion); + // In C# 13 and above, iterator bodies define a safe context even when nested in an unsafe context. + // In C# 12 and below, we keep the (spec violating) behavior that iterator bodies inherit the safe/unsafe context + // from their containing scope. Since there are errors for unsafe constructs directly in iterators, + // this inherited unsafe context can be observed only in nested non-iterator local functions. + var withoutUnsafe = isIteratorBody && this.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync); + + if (this.Flags.Includes(BinderFlags.UnsafeRegion)) + { + return withoutUnsafe + ? new Binder(this, this.Flags & ~BinderFlags.UnsafeRegion) + : this; + } + + return !withoutUnsafe && modifiers.Any(SyntaxKind.UnsafeKeyword) + ? new Binder(this, this.Flags | BinderFlags.UnsafeRegion) + : this; } internal Binder WithCheckedOrUncheckedRegion(bool @checked) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index e7c92744a4381..6ff5e6c47c6ba 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -882,7 +882,7 @@ private void ReportDynamicInvocationWarnings(SyntaxNode syntax, BoundMethodGroup private bool IsAmbiguousDynamicParamsArgument(ArrayBuilder arguments, MemberResolutionResult candidate, out SyntaxNode argumentSyntax) where TMethodOrPropertySymbol : Symbol { - if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember) && + if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember, out _) && candidate.Result.Kind == MemberResolutionKind.ApplicableInNormalForm) { var parameters = candidate.Member.GetParameters(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 23271214ea97a..d65ff54ae46fe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -176,9 +176,7 @@ private BoundStatement BindUnsafeStatement(UnsafeStatementSyntax node, BindingDi } else if (this.IsIndirectlyInIterator) // called *after* we know the binder map has been created. { - // Spec 8.2: "An iterator block always defines a safe context, even when its declaration - // is nested in an unsafe context." - Error(diagnostics, ErrorCode.ERR_IllegalInnerUnsafe, node.UnsafeKeyword); + CheckFeatureAvailability(node.UnsafeKeyword, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } return BindEmbeddedBlock(node.Block, diagnostics); @@ -268,6 +266,15 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi { Error(diagnostics, ErrorCode.ERR_YieldNotAllowedInScript, node.YieldKeyword); } + else if (InUnsafeRegion && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) + { + Error(diagnostics, ErrorCode.ERR_BadYieldInUnsafe, node.YieldKeyword); + } + // NOTE: Error conditions should be checked above this point; only warning conditions below. + else if (this.Flags.Includes(BinderFlags.InLockBody)) + { + Error(diagnostics, ErrorCode.WRN_BadYieldInLock, node.YieldKeyword); + } CheckRequiredLangVersionForIteratorMethods(node, diagnostics); return new BoundYieldReturnStatement(node, argument); @@ -1165,15 +1172,9 @@ protected BoundLocalDeclaration BindVariableDeclaration( protected bool CheckRefLocalInAsyncOrIteratorMethod(SyntaxToken identifierToken, BindingDiagnosticBag diagnostics) { - if (IsInAsyncMethod()) - { - Error(diagnostics, ErrorCode.ERR_BadAsyncLocalType, identifierToken); - return true; - } - else if (IsDirectlyInIterator) + if (IsDirectlyInIterator || IsInAsyncMethod()) { - Error(diagnostics, ErrorCode.ERR_BadIteratorLocalType, identifierToken); - return true; + return !CheckFeatureAvailability(identifierToken, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } return false; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs index ec8cd5d3885b7..2c4d9b6a7fb88 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs @@ -56,18 +56,16 @@ private CSDiagnosticInfo GetUnsafeDiagnosticInfo(TypeSymbol sizeOfTypeOpt) { return null; } - else if (this.IsIndirectlyInIterator) - { - // Spec 8.2: "An iterator block always defines a safe context, even when its declaration - // is nested in an unsafe context." - return new CSDiagnosticInfo(ErrorCode.ERR_IllegalInnerUnsafe); - } else if (!this.InUnsafeRegion) { return ((object)sizeOfTypeOpt == null) ? new CSDiagnosticInfo(ErrorCode.ERR_UnsafeNeeded) : new CSDiagnosticInfo(ErrorCode.ERR_SizeofUnsafe, sizeOfTypeOpt); } + else if (this.IsIndirectlyInIterator && MessageID.IDS_FeatureRefUnsafeInIteratorAsync.GetFeatureAvailabilityDiagnosticInfo(Compilation) is { } unsafeInIteratorDiagnosticInfo) + { + return unsafeInIteratorDiagnosticInfo; + } else { return null; diff --git a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs index edffa7f5535d3..e08152589f822 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs @@ -129,7 +129,7 @@ public static void ValidateIteratorMethod(CSharpCompilation compilation, MethodS if (((iterator as SourceMemberMethodSymbol)?.IsUnsafe == true || (iterator as LocalFunctionSymbol)?.IsUnsafe == true) && compilation.Options.AllowUnsafe) // Don't cascade { - diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, errorLocation); + MessageID.IDS_FeatureRefUnsafeInIteratorAsync.CheckFeatureAvailability(diagnostics, compilation, errorLocation); } var returnType = iterator.ReturnType; diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index 0695fe3f94395..1e6ee6bfc4953 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -416,7 +416,9 @@ public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax no ? new WithMethodTypeParametersBinder(match, _enclosing) : _enclosing; - binder = binder.WithUnsafeRegionIfNecessary(node.Modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(node.Modifiers, + isIteratorBody: match.IsIterator); + binder = new InMethodBinder(match, binder); } diff --git a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs index c29d038662a25..0899da0cfdb69 100644 --- a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs @@ -64,13 +64,6 @@ internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag dia _ = diagnostics.ReportUseSite(lockTypeInfo.EnterScopeMethod, exprSyntax) || diagnostics.ReportUseSite(lockTypeInfo.ScopeType, exprSyntax) || diagnostics.ReportUseSite(lockTypeInfo.ScopeDisposeMethod, exprSyntax); - - CheckRestrictedTypeInAsyncMethod( - originalBinder.ContainingMemberOrLambda, - lockTypeInfo.ScopeType, - diagnostics, - exprSyntax, - errorCode: ErrorCode.ERR_BadSpecialByRefLock); } BoundStatement stmt = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index f0a6c46065c3a..d60aa1ae31254 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -105,6 +105,7 @@ private init public readonly int BadParameter; public readonly MemberResolutionKind Kind; + public readonly TypeWithAnnotations DefinitionParamsElementTypeOpt; public readonly TypeWithAnnotations ParamsElementTypeOpt; /// @@ -121,11 +122,14 @@ private MemberAnalysisResult( int missingParameter = -1, bool hasAnyRefOmittedArgument = false, ImmutableArray constraintFailureDiagnosticsOpt = default, + TypeWithAnnotations definitionParamsElementTypeOpt = default, TypeWithAnnotations paramsElementTypeOpt = default) { + Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || definitionParamsElementTypeOpt.HasType); Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || paramsElementTypeOpt.HasType); this.Kind = kind; + this.DefinitionParamsElementTypeOpt = definitionParamsElementTypeOpt; this.ParamsElementTypeOpt = paramsElementTypeOpt; this.BadArgumentsOpt = badArgumentsOpt; this.ArgsToParamsOpt = argsToParamsOpt; @@ -314,7 +318,7 @@ public static MemberAnalysisResult UnsupportedMetadata() return new MemberAnalysisResult(MemberResolutionKind.UnsupportedMetadata); } - public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations paramsElementTypeOpt) + public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations definitionParamsElementTypeOpt, TypeWithAnnotations paramsElementTypeOpt) { Debug.Assert(conversions.Length != 0); Debug.Assert(badArguments.TrueBits().Any()); @@ -323,6 +327,7 @@ public static MemberAnalysisResult BadArgumentConversions(ImmutableArray ar badArguments, argsToParamsOpt, conversions, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } @@ -373,9 +378,11 @@ public static MemberAnalysisResult NormalForm(ImmutableArray argsToParamsOp return new MemberAnalysisResult(MemberResolutionKind.ApplicableInNormalForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument); } - public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations paramsElementType) + public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations definitionParamsElementType, TypeWithAnnotations paramsElementType) { - return new MemberAnalysisResult(MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, paramsElementTypeOpt: paramsElementType); + return new MemberAnalysisResult( + MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, definitionParamsElementTypeOpt: definitionParamsElementType, paramsElementTypeOpt: paramsElementType); } public static MemberAnalysisResult Worse() diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 385db1686b016..5acde0dfa8f0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -817,9 +817,9 @@ private void AddConstructorToCandidateSet(MethodSymbol constructor, ArrayBuilder var result = normalResult; if (!normalResult.IsValid) { - if (IsValidParams(_binder, constructor)) + if (IsValidParams(_binder, constructor, out TypeWithAnnotations definitionElementType)) { - var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, completeResults, ref useSiteInfo); + var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, definitionElementType, completeResults, ref useSiteInfo); if (expandedResult.IsValid || completeResults) { result = expandedResult; @@ -864,6 +864,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( return IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -878,6 +879,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( private MemberAnalysisResult IsConstructorApplicableInExpandedForm( MethodSymbol constructor, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool completeResults, ref CompoundUseSiteInfo useSiteInfo) { @@ -906,6 +908,7 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm( var result = IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: definitionParamsElementType, isExpanded: true, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -1038,7 +1041,7 @@ private void AddMemberToCandidateSet( // Second, we need to determine if the method is applicable in its normal form or its expanded form. - var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember)) + var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember, out _)) ? default(MemberResolutionResult) : IsMemberApplicableInNormalForm( member, @@ -1057,13 +1060,14 @@ private void AddMemberToCandidateSet( // tricks you can pull to make overriding methods [indexers] inconsistent with overridden // methods [indexers] (or implementing methods [indexers] inconsistent with interfaces). - if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember)) + if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember, out TypeWithAnnotations definitionElementType)) { var expandedResult = IsMemberApplicableInExpandedForm( member, leastOverriddenMember, typeArguments, arguments, + definitionElementType, allowRefOmittedArguments: (options & Options.AllowRefOmittedArguments) != 0, completeResults: completeResults, dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, @@ -1156,17 +1160,19 @@ static bool haveBadArgumentForLastParameter(MemberResolutionResult resu // We need to know if this is a valid formal parameter list with a parameter array // as the final formal parameter. We might be in an error recovery scenario // where the params array is not an array type. - public static bool IsValidParams(Binder binder, Symbol member) + public static bool IsValidParams(Binder binder, Symbol member, out TypeWithAnnotations definitionElementType) { // A varargs method is never a valid params method. if (member.GetIsVararg()) { + definitionElementType = default; return false; } int paramCount = member.GetParameterCount(); if (paramCount == 0) { + definitionElementType = default; return false; } @@ -1175,9 +1181,10 @@ public static bool IsValidParams(Binder binder, Symbol member) (final.IsParamsCollection && !final.Type.IsSZArray() && (binder.Compilation.LanguageVersion > LanguageVersion.CSharp12 || member.ContainingModule == binder.Compilation.SourceModule))) { - return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out _); + return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out definitionElementType); } + definitionElementType = default; return false; } @@ -1837,25 +1844,6 @@ private void RemoveWorseMembers(ArrayBuilder - /// Returns the parameter type (considering params). - /// - private static TypeSymbol GetParameterType(ParameterSymbol parameter, MemberAnalysisResult result, int parameterCount) - { - var type = parameter.Type; - if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && - parameter.Ordinal == parameterCount - 1) - { - Debug.Assert(result.ParamsElementTypeOpt.HasType); - Debug.Assert(result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); - return result.ParamsElementTypeOpt.Type; - } - else - { - return type; - } - } - /// /// Returns the parameter corresponding to the given argument index. /// @@ -1955,11 +1943,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out RefKind parameter1RefKind); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out RefKind parameter2RefKind); bool okToDowngradeToNeither; BetterResult r; @@ -1967,10 +1953,10 @@ private BetterResult BetterFunctionMember( r = BetterConversionFromExpression(arguments[i], type1, m1.Result.ConversionForArg(i), - parameter1.RefKind, + parameter1RefKind, type2, m2.Result.ConversionForArg(i), - parameter2.RefKind, + parameter2RefKind, considerRefKinds, ref useSiteInfo, out okToDowngradeToNeither); @@ -2099,11 +2085,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out _); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out _); var type1Normalized = type1; var type2Normalized = type2; @@ -2248,8 +2232,8 @@ private BetterResult BetterFunctionMember( using (var uninst1 = TemporaryArray.Empty) using (var uninst2 = TemporaryArray.Empty) { - var m1Original = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); - var m2Original = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m1DefinitionParameters = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m2DefinitionParameters = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); for (i = 0; i < arguments.Count; ++i) { // If these are both applicable varargs methods and we're looking at the __arglist argument @@ -2261,11 +2245,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1Original); - uninst1.Add(GetParameterType(parameter1, m1.Result, m1Original.Length)); + uninst1.Add(getParameterTypeAndRefKind(i, m1.Result, m1DefinitionParameters, m1.Result.DefinitionParamsElementTypeOpt, out _)); - var parameter2 = GetParameter(i, m2.Result, m2Original); - uninst2.Add(GetParameterType(parameter2, m2.Result, m2Original.Length)); + uninst2.Add(getParameterTypeAndRefKind(i, m2.Result, m2DefinitionParameters, m2.Result.DefinitionParamsElementTypeOpt, out _)); } result = MoreSpecificType(ref uninst1.AsRef(), ref uninst2.AsRef(), ref useSiteInfo); @@ -2353,6 +2335,26 @@ private BetterResult BetterFunctionMember( } return BetterResult.Neither; + + // Returns the parameter type (considering params). + static TypeSymbol getParameterTypeAndRefKind(int i, MemberAnalysisResult result, ImmutableArray parameters, TypeWithAnnotations paramsElementTypeOpt, out RefKind parameter1RefKind) + { + var parameter = GetParameter(i, result, parameters); + parameter1RefKind = parameter.RefKind; + + var type = parameter.Type; + if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && + parameter.Ordinal == parameters.Length - 1) + { + Debug.Assert(paramsElementTypeOpt.HasType); + Debug.Assert(paramsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); + return paramsElementTypeOpt.Type; + } + else + { + return type; + } + } } /// @@ -3691,7 +3693,7 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( TMember leastOverriddenMemberConstructedFrom = GetConstructedFrom(leastOverriddenMember); bool hasAnyRefOmittedArgument; - EffectiveParameters originalEffectiveParameters = GetEffectiveParametersInNormalForm( + EffectiveParameters constructedFromEffectiveParameters = GetEffectiveParametersInNormalForm( leastOverriddenMemberConstructedFrom, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, @@ -3706,7 +3708,8 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( // The applicability is checked based on effective parameters passed in. var applicableResult = IsApplicable( member, leastOverriddenMemberConstructedFrom, - typeArguments, arguments, originalEffectiveParameters, + typeArguments, arguments, constructedFromEffectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, @@ -3730,6 +3733,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm typeArguments, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool allowRefOmittedArguments, bool completeResults, bool dynamicConvertsToAnything, @@ -3754,7 +3758,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm IsMemberApplicableInExpandedForm IsApplicable( TMember leastOverriddenMember, // method or property ArrayBuilder typeArgumentsBuilder, AnalyzedArguments arguments, - EffectiveParameters originalEffectiveParameters, + EffectiveParameters constructedFromEffectiveParameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, ImmutableArray argsToParamsMap, bool hasAnyRefOmittedArgument, @@ -3836,7 +3842,7 @@ private MemberResolutionResult IsApplicable( typeArguments = InferMethodTypeArguments(method, leastOverriddenMethod.ConstructedFrom.TypeParameters, arguments, - originalEffectiveParameters, + constructedFromEffectiveParameters, out hasTypeArgumentsInferredFromFunctionType, out inferenceError, ref useSiteInfo); @@ -3892,19 +3898,20 @@ private MemberResolutionResult IsApplicable( var map = new TypeMap(leastOverriddenMethod.TypeParameters, typeArguments, allowAlpha: true); constructedEffectiveParameters = new EffectiveParameters( - map.SubstituteTypes(originalEffectiveParameters.ParameterTypes), - originalEffectiveParameters.ParameterRefKinds, - originalEffectiveParameters.FirstParamsElementIndex); + map.SubstituteTypes(constructedFromEffectiveParameters.ParameterTypes), + constructedFromEffectiveParameters.ParameterRefKinds, + constructedFromEffectiveParameters.FirstParamsElementIndex); } else { - constructedEffectiveParameters = originalEffectiveParameters; + constructedEffectiveParameters = constructedFromEffectiveParameters; ignoreOpenTypes = false; } var applicableResult = IsApplicable( member, constructedEffectiveParameters, + definitionParamsElementTypeOpt, isExpanded, arguments, argsToParamsMap, @@ -3975,6 +3982,7 @@ private ImmutableArray InferMethodTypeArguments( private MemberAnalysisResult IsApplicable( Symbol candidate, // method or property EffectiveParameters parameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, AnalyzedArguments arguments, ImmutableArray argsToParameters, @@ -4113,7 +4121,8 @@ private MemberAnalysisResult IsApplicable( // of overloads for the semantic model. Debug.Assert(badArguments.IsNull); Debug.Assert(conversions == null); - return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), paramsElementTypeOpt); + return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } if (!conversion.Exists) @@ -4148,12 +4157,16 @@ private MemberAnalysisResult IsApplicable( var conversionsArray = conversions != null ? conversions.ToImmutableAndFree() : default(ImmutableArray); if (!badArguments.IsNull) { - result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, paramsElementTypeOpt); + result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, + paramsElementTypeOpt: paramsElementTypeOpt); } else if (isExpanded) { Debug.Assert(paramsElementTypeOpt.HasType); - result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, paramsElementTypeOpt); + result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, + definitionParamsElementType: definitionParamsElementTypeOpt, + paramsElementType: paramsElementTypeOpt); } else { diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 775774fe0188c..8fb8f0c4eb400 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -119,7 +119,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo Debug.Assert(expressionOpt is not null); if (expressionOpt.Type is not null) { - CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax, errorCode: ErrorCode.ERR_BadSpecialByRefUsing); + CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax); } } else diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4844a5a394ba0..a213bb7fb05c9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2950,9 +2950,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Cannot use ref, out, or in parameter '{0}' inside an anonymous method, lambda expression, query expression, or local function - - Unsafe code may not appear in iterators - Cannot yield a value in the body of a catch clause @@ -3810,7 +3807,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi __arglist is not allowed in the parameter list of async methods - 'await' cannot be used in an expression containing the type '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. Async methods cannot have pointer type parameters @@ -3851,15 +3848,12 @@ Give the compiler some way to differentiate the methods. For example, you can gi The 'async' modifier can only be used in methods that have a body. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Security attribute '{0}' cannot be applied to an Async method. @@ -5290,12 +5284,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot use ref local '{0}' inside an anonymous method, lambda expression, or query expression - - Iterators cannot have by-reference locals - - - Async methods cannot have by-reference locals - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. @@ -7857,9 +7845,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Lock object @@ -7929,6 +7914,24 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The data argument to InterceptsLocationAttribute refers to an invalid position in file '{0}'. + + 'yield return' should not be used in the body of a lock statement + + + 'yield return' should not be used in the body of a lock statement + + + ref and unsafe in async and iterator methods + + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + + Cannot use 'yield return' in an 'unsafe' block + + + The '&' operator cannot be used on parameters or local variables in iterator methods. + Partial property '{0}' must have an implementation part. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 9a7e766e6e29f..902be8a824b7d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -792,7 +792,7 @@ internal enum ErrorCode ERR_BadYieldInTryOfCatch = 1626, ERR_EmptyYield = 1627, ERR_AnonDelegateCantUse = 1628, - ERR_IllegalInnerUnsafe = 1629, + // ERR_IllegalInnerUnsafe = 1629, //ERR_BadWatsonMode = 1630, ERR_BadYieldInCatch = 1631, ERR_BadDelegateLeave = 1632, @@ -1111,7 +1111,7 @@ internal enum ErrorCode ERR_NonTaskMainCantBeAsync = 4009, ERR_CantConvAsyncAnonFuncReturns = 4010, ERR_BadAwaiterPattern = 4011, - ERR_BadSpecialByRefLocal = 4012, + ERR_BadSpecialByRefParameter = 4012, ERR_SpecialByRefInLambda = 4013, WRN_UnobservedAwaitableExpression = 4014, ERR_SynchronizedAsyncMethod = 4015, @@ -1429,8 +1429,8 @@ internal enum ErrorCode ERR_RefAssignmentMustHaveIdentityConversion = 8173, ERR_ByReferenceVariableMustBeInitialized = 8174, ERR_AnonDelegateCantUseLocal = 8175, - ERR_BadIteratorLocalType = 8176, - ERR_BadAsyncLocalType = 8177, + // ERR_BadIteratorLocalType = 8176, + // ERR_BadAsyncLocalType = 8177, ERR_RefReturningCallAndAwait = 8178, #endregion diagnostics for ref locals and ref returns introduced in C# 7 @@ -2161,7 +2161,7 @@ internal enum ErrorCode ERR_UnscopedRefAttributeUnsupportedMemberTarget = 9101, ERR_UnscopedRefAttributeInterfaceImplementation = 9102, ERR_UnrecognizedRefSafetyRulesAttributeVersion = 9103, - ERR_BadSpecialByRefUsing = 9104, + // ERR_BadSpecialByRefUsing = 9104, ERR_InvalidPrimaryConstructorParameterReference = 9105, ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver = 9106, @@ -2286,7 +2286,7 @@ internal enum ErrorCode ERR_CollectionExpressionMissingAdd = 9215, WRN_ConvertingLock = 9216, - ERR_BadSpecialByRefLock = 9217, + ERR_RefLocalAcrossAwait = 9217, ERR_DynamicDispatchToParamsCollection = 9218, ERR_ParamsCollectionAmbiguousDynamicArgument = 9219, @@ -2324,6 +2324,10 @@ internal enum ErrorCode #endregion + WRN_BadYieldInLock = 9237, + ERR_BadYieldInUnsafe = 9238, + ERR_AddressOfInIterator = 9239, + // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 32efb6f8fb00e..405e19c2906ff 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -207,6 +207,10 @@ internal static int GetWarningLevel(ErrorCode code) // docs/compilers/CSharp/Warnversion Warning Waves.md switch (code) { + case ErrorCode.WRN_BadYieldInLock: + // Warning level 9 is exclusively for warnings introduced in the compiler + // shipped with dotnet 9 (C# 13) and that can be reported for pre-existing code. + return 9; case ErrorCode.WRN_AddressOfInAsync: case ErrorCode.WRN_ByValArraySizeConstRequired: // Warning level 8 is exclusively for warnings introduced in the compiler @@ -624,6 +628,7 @@ or ErrorCode.ERR_InterceptorCannotBeGeneric or ErrorCode.ERR_InterceptableMethodMustBeOrdinary or ErrorCode.ERR_PossibleAsyncIteratorWithoutYield or ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait + or ErrorCode.ERR_RefLocalAcrossAwait // Update src\EditorFeatures\CSharp\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs // whenever new values are added here. => true, @@ -1276,7 +1281,6 @@ or ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRef or ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike or ErrorCode.ERR_AnonDelegateCantUseStructPrimaryConstructorParameterInMember or ErrorCode.ERR_AnonDelegateCantUseStructPrimaryConstructorParameterCaptured - or ErrorCode.ERR_IllegalInnerUnsafe or ErrorCode.ERR_BadYieldInCatch or ErrorCode.ERR_BadDelegateLeave or ErrorCode.WRN_IllegalPragma @@ -1536,7 +1540,7 @@ or ErrorCode.ERR_BadAwaitArgVoidCall or ErrorCode.ERR_NonTaskMainCantBeAsync or ErrorCode.ERR_CantConvAsyncAnonFuncReturns or ErrorCode.ERR_BadAwaiterPattern - or ErrorCode.ERR_BadSpecialByRefLocal + or ErrorCode.ERR_BadSpecialByRefParameter or ErrorCode.WRN_UnobservedAwaitableExpression or ErrorCode.ERR_SynchronizedAsyncMethod or ErrorCode.ERR_BadAsyncReturnExpression @@ -1779,8 +1783,6 @@ or ErrorCode.ERR_InitializeByReferenceVariableWithValue or ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion or ErrorCode.ERR_ByReferenceVariableMustBeInitialized or ErrorCode.ERR_AnonDelegateCantUseLocal - or ErrorCode.ERR_BadIteratorLocalType - or ErrorCode.ERR_BadAsyncLocalType or ErrorCode.ERR_PredefinedValueTupleTypeNotFound or ErrorCode.ERR_SemiOrLBraceOrArrowExpected or ErrorCode.ERR_NewWithTupleTypeSyntax @@ -2332,7 +2334,6 @@ or ErrorCode.WRN_ParamsArrayInLambdaOnly or ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget or ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation or ErrorCode.ERR_UnrecognizedRefSafetyRulesAttributeVersion - or ErrorCode.ERR_BadSpecialByRefUsing or ErrorCode.ERR_InvalidPrimaryConstructorParameterReference or ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver or ErrorCode.WRN_CapturedPrimaryConstructorParameterPassedToBase @@ -2421,7 +2422,6 @@ or ErrorCode.ERR_CollectionExpressionTargetNoElementType or ErrorCode.ERR_CollectionExpressionMissingConstructor or ErrorCode.ERR_CollectionExpressionMissingAdd or ErrorCode.WRN_ConvertingLock - or ErrorCode.ERR_BadSpecialByRefLock or ErrorCode.ERR_DynamicDispatchToParamsCollection or ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument or ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod @@ -2441,6 +2441,9 @@ or ErrorCode.ERR_InterceptsLocationDuplicateFile or ErrorCode.ERR_InterceptsLocationFileNotFound or ErrorCode.ERR_InterceptsLocationDataInvalidPosition or ErrorCode.INF_TooManyBoundLambdas + or ErrorCode.WRN_BadYieldInLock + or ErrorCode.ERR_BadYieldInUnsafe + or ErrorCode.ERR_AddressOfInIterator or ErrorCode.ERR_PartialPropertyMissingImplementation or ErrorCode.ERR_PartialPropertyMissingDefinition diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 72f876baaa909..eb580e98e2ac6 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -282,6 +282,8 @@ internal enum MessageID IDS_FeatureLockObject = MessageBase + 12841, IDS_FeatureParamsCollections = MessageBase + 12842, + + IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843, } // Message IDs may refer to strings that need to be localized. @@ -466,6 +468,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureImplicitIndexerInitializer: case MessageID.IDS_FeatureLockObject: case MessageID.IDS_FeatureParamsCollections: + case MessageID.IDS_FeatureRefUnsafeInIteratorAsync: return LanguageVersion.Preview; // C# 12.0 features. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 2abba14887c5d..c139e3daf28c4 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6719,27 +6719,26 @@ private ImmutableArray VisitArguments( (ParameterSymbol? parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool isExpandedParamsArgument) = GetCorrespondingParameter(i, parametersOpt, argsToParamsOpt, expanded, ref paramsIterationType); - if (// This is known to happen for certain error scenarios, because - // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` - // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. - parameter is null || - // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type - // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. - node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true }) + // This is known to happen for certain error scenarios, because + // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` + // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. + if (parameter is null) { - if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + if (tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) { - // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it - // (we won't be able to complete that continuation) - // We flush the completion with a plausible/dummy type and remove it. - completion(TypeWithAnnotations.Create(argument.Type)); - TargetTypedAnalysisCompletion.Remove(argumentNoConversion); - - Debug.Assert(parameter is not null || method is ErrorMethodSymbol); + Debug.Assert(method is ErrorMethodSymbol); } continue; } + // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type + // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. + if (node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true } && + tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) + { + continue; + } + // We disable diagnostics when: // 1. the containing call has errors (to reduce cascading diagnostics) // 2. on implicit default arguments (since that's really only an issue with the declaration) @@ -6926,6 +6925,21 @@ static void expandParamsCollection(ref ImmutableArray arguments } } } + + bool tryShortCircuitTargetTypedExpression(BoundExpression argument, BoundExpression argumentNoConversion) + { + if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + { + // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it + // (we won't be able to complete that continuation) + // We flush the completion with a plausible/dummy type and remove it. + completion(TypeWithAnnotations.Create(argument.Type)); + TargetTypedAnalysisCompletion.Remove(argumentNoConversion); + return true; + } + + return false; + } } private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbol? method) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index a5ce638524661..7005afb927341 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -931,6 +931,18 @@ interpolation_format_clause : ':' interpolated_string_text_token ; +interpolated_multi_line_raw_string_start_token + : '$'+ '"""' '"'* + ; + +interpolated_raw_string_end_token + : '"""' '"'* /* must match number of quotes in raw_string_start_token */ + ; + +interpolated_single_line_raw_string_start_token + : '$'+ '"""' '"'* + ; + invocation_expression : expression argument_list ; @@ -940,19 +952,31 @@ is_pattern_expression ; literal_expression - : '__arglist' - | 'default' + : 'default' | 'false' | 'null' | 'true' + | '__arglist' | character_literal_token | multi_line_raw_string_literal_token | numeric_literal_token | single_line_raw_string_literal_token | string_literal_token - | utf_8_multi_line_raw_string_literal_token - | utf_8_single_line_raw_string_literal_token - | utf_8_string_literal_token + | utf8_multi_line_raw_string_literal_token + | utf8_single_line_raw_string_literal_token + | utf8_string_literal_token + ; + +utf8_multi_line_raw_string_literal_token + : multi_line_raw_string_literal_token ('U8' | 'u8') + ; + +utf8_single_line_raw_string_literal_token + : single_line_raw_string_literal_token ('U8' | 'u8') + ; + +utf8_string_literal_token + : string_literal_token ('U8' | 'u8') ; make_ref_expression @@ -1101,22 +1125,22 @@ with_expression ; xml_node - : xml_c_data_section - | xml_comment + : xml_comment + | xml_c_data_section | xml_element | xml_empty_element | xml_processing_instruction | xml_text ; -xml_c_data_section - : '' - ; - xml_comment : '' ; +xml_c_data_section + : '' + ; + xml_element : xml_element_start_tag xml_node* xml_element_end_tag ; @@ -1347,84 +1371,388 @@ skipped_tokens_trivia : syntax_token* ; -base_argument_list - : argument_list - | bracketed_argument_list +syntax_token + : character_literal_token + | identifier_token + | keyword + | numeric_literal_token + | operator_token + | punctuation_token + | string_literal_token ; -base_cref_parameter_list - : cref_bracketed_parameter_list - | cref_parameter_list +identifier_token + : '@'? identifier_start_character identifier_part_character ; -base_parameter_list - : bracketed_parameter_list - | parameter_list +identifier_start_character + : letter_character + | underscore_character ; -base_parameter - : function_pointer_parameter - | parameter +letter_character + : /* [\p{L}\p{Nl}] category letter, all subcategories; category number, subcategory letter */ + | unicode_escape_sequence /* only escapes for categories L & Nl allowed */ ; -character_literal_token - : /* see lexical specification */ +underscore_character + : '\\u005' /* unicode_escape_sequence for underscore */ + | '_' ; -expression_or_pattern - : expression - | pattern +identifier_part_character + : combining_character + | connecting_character + | decimal_digit_character + | formatting_character + | letter_character ; -identifier_token - : /* see lexical specification */ +combining_character + : /* [\p{Mn}\p{Mc}] category Mark, subcategories non-spacing and spacing combining */ + | unicode_escape_sequence /* only escapes for categories Mn & Mc allowed */ ; -interpolated_multi_line_raw_string_start_token - : /* see lexical specification */ +connecting_character + : /* [\p{Pc}] category Punctuation, subcategory connector */ + | unicode_escape_sequence /* only escapes for category Pc allowed */ ; -interpolated_raw_string_end_token - : /* see lexical specification */ +decimal_digit_character + : /* [\p{Nd}] category number, subcategory decimal digit */ + | unicode_escape_sequence /* only escapes for category Nd allowed */ ; -interpolated_single_line_raw_string_start_token - : /* see lexical specification */ +formatting_character + : /* [\p{Cf}] category Other, subcategory format. */ + | unicode_escape_sequence /* only escapes for category Cf allowed */ ; -interpolated_string_text_token - : /* see lexical specification */ +keyword + : 'as' + | 'base' + | 'bool' + | 'break' + | 'byte' + | 'case' + | 'catch' + | 'char' + | 'checked' + | 'class' + | 'continue' + | 'decimal' + | 'default' + | 'delegate' + | 'do' + | 'double' + | 'else' + | 'enum' + | 'event' + | 'explicit' + | 'false' + | 'finally' + | 'float' + | 'for' + | 'foreach' + | 'goto' + | 'if' + | 'implicit' + | 'in' + | 'int' + | 'interface' + | 'is' + | 'lock' + | 'long' + | 'namespace' + | 'null' + | 'object' + | 'operator' + | 'out' + | 'params' + | 'return' + | 'sbyte' + | 'short' + | 'sizeof' + | 'stackalloc' + | 'string' + | 'struct' + | 'switch' + | 'this' + | 'throw' + | 'true' + | 'try' + | 'typeof' + | 'uint' + | 'ulong' + | 'unchecked' + | 'ushort' + | 'using' + | 'void' + | 'while' + | '__arglist' + | '__makeref' + | '__reftype' + | '__refvalue' + | modifier ; -multi_line_raw_string_literal_token - : /* see lexical specification */ +numeric_literal_token + : integer_literal_token + | real_literal_token ; -numeric_literal_token - : /* see lexical specification */ +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token ; -single_line_raw_string_literal_token - : /* see lexical specification */ +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + ; + +integer_type_suffix + : 'L' + | 'l' + | 'LU' + | 'lU' + | 'Lu' + | 'lu' + | 'U' + | 'u' + | 'UL' + | 'uL' + | 'Ul' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? + ; + +hexadecimal_digit + : 'A' + | 'a' + | 'B' + | 'b' + | 'C' + | 'c' + | 'D' + | 'd' + | 'E' + | 'e' + | 'F' + | 'f' + | decimal_digit + ; + +real_literal_token + : '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ exponent_part real_type_suffix? + | decimal_digit+ real_type_suffix + ; + +exponent_part + : ('E' | 'e') ('+' | '-')? decimal_digit+ + ; + +real_type_suffix + : 'D' + | 'd' + | 'F' + | 'f' + | 'M' + | 'm' + ; + +character_literal_token + : '\'' character '\'' + ; + +character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_character + | unicode_escape_sequence + ; + +hexadecimal_escape_sequence + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? + ; + +simple_escape_sequence + : '\\"' + | '\\0' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\t' + | '\\v' + | '\\\'' + | '\\\\' + ; + +single_character + : /* ~['\\\u000D\u000A\u0085\u2028\u2029] anything but ', \\, and new_line_character */ + ; + +unicode_escape_sequence + : '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + | '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit ; string_literal_token - : /* see lexical specification */ + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +single_regular_string_literal_character + : /* ~["\\\u000D\u000A\u0085\u2028\u2029] anything but ", \, and new_line_character */ + ; + +verbatim_string_literal_token + : '@"' verbatim_string_literal_character* '"' + ; + +verbatim_string_literal_character + : quote_escape_sequence + | single_verbatim_string_literal_character + ; + +quote_escape_sequence + : '""' + ; + +single_verbatim_string_literal_character + : /* anything but quotation mark (U+0022) */ + ; + +operator_token + : '!' + | '!=' + | '%' + | '%=' + | '&&' + | '&' + | '&=' + | '*' + | '*=' + | '+' + | '++' + | '+=' + | '-' + | '--' + | '-=' + | '/' + | '/=' + | '<' + | '<<' + | '<<=' + | '<=' + | '=' + | '==' + | '>' + | '>=' + | '>>' + | '>>=' + | '>>>' + | '>>>=' + | '??' + | '??=' + | 'as' + | 'is' + | '^' + | '^=' + | '|' + | '|=' + | '||' + | '~' + ; + +punctuation_token + : '"' + | '#' + | '(' + | ')' + | ',' + | '->' + | '.' + | '..' + | '/>' + | ':' + | '::' + | ';' + | '' + | '?' + | '[' + | '\'' + | '\\' + | ']' + | '{' + | '}' ; -syntax_token - : /* see lexical specification */ +base_argument_list + : argument_list + | bracketed_argument_list + ; + +base_cref_parameter_list + : cref_bracketed_parameter_list + | cref_parameter_list + ; + +base_parameter_list + : bracketed_parameter_list + | parameter_list + ; + +base_parameter + : function_pointer_parameter + | parameter ; -utf_8_multi_line_raw_string_literal_token +expression_or_pattern + : expression + | pattern + ; + +interpolated_string_text_token : /* see lexical specification */ ; -utf_8_single_line_raw_string_literal_token +multi_line_raw_string_literal_token : /* see lexical specification */ ; -utf_8_string_literal_token +single_line_raw_string_literal_token : /* see lexical specification */ ; diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index c1a73bb4e3936..9475412e96ddf 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -342,6 +342,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer: case ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor: case ErrorCode.WRN_PartialPropertySignatureDifference: + case ErrorCode.WRN_BadYieldInLock: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index ef6e145345c23..6cbecefe3f188 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -195,8 +195,7 @@ private BoundExpression VisitArrayOrSpanCollectionExpression(BoundCollectionExpr syntax, elementType, elements, - asReadOnlySpan: collectionTypeKind == CollectionExpressionTypeKind.ReadOnlySpan, - _additionalLocals); + asReadOnlySpan: collectionTypeKind == CollectionExpressionTypeKind.ReadOnlySpan); } Debug.Assert(IsAllocatingRefStructCollectionExpression(node, collectionTypeKind, elementType.Type, _compilation)); @@ -243,7 +242,7 @@ private BoundExpression VisitCollectionInitializerCollectionExpression(BoundColl // Create a temp for the collection. BoundAssignmentOperator assignmentToTemp; - BoundLocal temp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, isKnownToReferToTempIfReferenceType: true); + BoundLocal temp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp); var sideEffects = ArrayBuilder.GetInstance(elements.Length + 1); sideEffects.Add(assignmentToTemp); @@ -429,16 +428,33 @@ private BoundExpression CreateAndPopulateSpanFromInlineArray( SyntaxNode syntax, TypeWithAnnotations elementType, ImmutableArray elements, - bool asReadOnlySpan, - ArrayBuilder locals) + bool asReadOnlySpan) { Debug.Assert(elements.Length > 0); Debug.Assert(elements.All(e => e is BoundExpression)); Debug.Assert(_factory.ModuleBuilderOpt is { }); Debug.Assert(_diagnostics.DiagnosticBag is { }); Debug.Assert(_compilation.Assembly.RuntimeSupportsInlineArrayTypes); + Debug.Assert(_additionalLocals is { }); int arrayLength = elements.Length; + if (arrayLength == 1 + && _factory.WellKnownMember(asReadOnlySpan + ? WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T + : WellKnownMember.System_Span_T__ctor_ref_T, isOptional: true) is MethodSymbol spanRefConstructor) + { + // Special case: no need to create an InlineArray1 type. Just use a temp of the element type. + var spanType = _factory + .WellKnownType(asReadOnlySpan ? WellKnownType.System_ReadOnlySpan_T : WellKnownType.System_Span_T) + .Construct([elementType]); + var constructor = spanRefConstructor.AsMember(spanType); + var element = VisitExpression((BoundExpression)elements[0]); + var temp = _factory.StoreToTemp(element, out var assignment); + _additionalLocals.Add(temp.LocalSymbol); + var call = _factory.New(constructor, arguments: [temp], argumentRefKinds: [asReadOnlySpan ? RefKindExtensions.StrictIn : RefKind.Ref]); + return _factory.Sequence([assignment], call); + } + var inlineArrayType = _factory.ModuleBuilderOpt.EnsureInlineArrayTypeExists(syntax, _factory, arrayLength, _diagnostics.DiagnosticBag).Construct(ImmutableArray.Create(elementType)); Debug.Assert(inlineArrayType.HasInlineArrayAttribute(out int inlineArrayLength) && inlineArrayLength == arrayLength); @@ -449,10 +465,10 @@ private BoundExpression CreateAndPopulateSpanFromInlineArray( // Create an inline array and assign to a local. // var tmp = new <>y__InlineArrayN(); BoundAssignmentOperator assignmentToTemp; - BoundLocal inlineArrayLocal = _factory.StoreToTemp(new BoundDefaultExpression(syntax, inlineArrayType), out assignmentToTemp, isKnownToReferToTempIfReferenceType: true); + BoundLocal inlineArrayLocal = _factory.StoreToTemp(new BoundDefaultExpression(syntax, inlineArrayType), out assignmentToTemp); var sideEffects = ArrayBuilder.GetInstance(); sideEffects.Add(assignmentToTemp); - locals.Add(inlineArrayLocal.LocalSymbol); + _additionalLocals.Add(inlineArrayLocal.LocalSymbol); // Populate the inline array. // InlineArrayElementRef<<>y__InlineArrayN, ElementType>(ref tmp, 0) = element0; @@ -604,8 +620,7 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType // int index = 0; BoundLocal indexTemp = _factory.StoreToTemp( _factory.Literal(0), - out assignmentToTemp, - isKnownToReferToTempIfReferenceType: true); + out assignmentToTemp); localsBuilder.Add(indexTemp); sideEffects.Add(assignmentToTemp); @@ -615,8 +630,7 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType ImmutableArray.Create(GetKnownLengthExpression(elements, numberIncludingLastSpread, localsBuilder)), initializerOpt: null, arrayType), - out assignmentToTemp, - isKnownToReferToTempIfReferenceType: true); + out assignmentToTemp); localsBuilder.Add(arrayTemp); sideEffects.Add(assignmentToTemp); @@ -903,7 +917,7 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty // If we use optimizations, we know the length of the resulting list, and we store it in a temp so we can pass it to List.ctor(int32) and to CollectionsMarshal.SetCount // int knownLengthTemp = N + s1.Length + ...; - knownLengthTemp = _factory.StoreToTemp(knownLengthExpression, out assignmentToTemp, isKnownToReferToTempIfReferenceType: true); + knownLengthTemp = _factory.StoreToTemp(knownLengthExpression, out assignmentToTemp); localsBuilder.Add(knownLengthTemp); sideEffects.Add(assignmentToTemp); @@ -924,7 +938,7 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty } // Create a temp for the list. - BoundLocal listTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, isKnownToReferToTempIfReferenceType: true); + BoundLocal listTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp); localsBuilder.Add(listTemp); sideEffects.Add(assignmentToTemp); @@ -940,7 +954,7 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty sideEffects.Add(_factory.Call(receiver: null, setCount, listTemp, knownLengthTemp)); // var span = CollectionsMarshal.AsSpan /// /// Data flow analysis is used to calculate the locals. At yield/await we mark all variables as "unassigned". - /// When a read from an unassigned variables is reported we add the variable to the captured set. + /// When a read from an unassigned variable is reported we add the variable to the captured set. /// "this" parameter is captured if a reference to "this", "base" or an instance field is encountered. /// Variables used in finally also need to be captured if there is a yield in the corresponding try block. /// @@ -76,19 +76,33 @@ public static OrderedSet Analyze(CSharpCompilation compilation, MethodSy foreach (var kvp in lazyDisallowedCaptures) { var variable = kvp.Key; - var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type; - if (variable is SynthesizedLocal local && local.SynthesizedKind == SynthesizedLocalKind.Spill) + if (variable is LocalSymbol local) { - Debug.Assert(local.TypeWithAnnotations.IsRestrictedType()); - diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, local.GetFirstLocation(), local.TypeWithAnnotations); + foreach (var syntax in kvp.Value) + { + if (local.TypeWithAnnotations.IsRestrictedType()) + { + // CS4007: Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, syntax.Location, local.TypeWithAnnotations); + } + else + { + Debug.Assert(local.RefKind != RefKind.None); + // CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_RefLocalAcrossAwait, syntax.Location); + } + } } else { - foreach (CSharpSyntaxNode syntax in kvp.Value) + var parameter = (ParameterSymbol)variable; + Debug.Assert(parameter.TypeWithAnnotations.IsRestrictedType()); + + foreach (var syntax in kvp.Value) { - // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method - diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, type); + // CS4007: Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, syntax.Location, parameter.TypeWithAnnotations); } } } @@ -195,14 +209,23 @@ protected override ImmutableArray Scan(ref bool badRegion) private void CaptureVariable(Symbol variable, SyntaxNode syntax) { var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type; - if (type.IsRestrictedType()) + if (type.IsRestrictedType() || + (variable is LocalSymbol { RefKind: not RefKind.None } refLocal && !canRefLocalBeHoisted(refLocal))) { (_lazyDisallowedCaptures ??= new MultiDictionary()).Add(variable, syntax); } else { if (_variablesToHoist.Add(variable) && variable is LocalSymbol local && _boundRefLocalInitializers.TryGetValue(local, out var variableInitializer)) - CaptureRefInitializer(variableInitializer, syntax); + CaptureRefInitializer(variableInitializer, local.SynthesizedKind != SynthesizedLocalKind.UserDefined ? variableInitializer.Syntax : syntax); + } + + static bool canRefLocalBeHoisted(LocalSymbol refLocal) + { + return refLocal.SynthesizedKind == SynthesizedLocalKind.Spill || + (refLocal.SynthesizedKind == SynthesizedLocalKind.ForEachArray && + refLocal.Type.HasInlineArrayAttribute(out _) && + refLocal.Type.TryGetInlineArrayElementField() is not null); } } diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index ec3d3a6db7114..382051fc43afc 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -789,6 +789,21 @@ public BoundObjectCreationExpression New(NamedTypeSymbol type, ImmutableArray args) => new BoundObjectCreationExpression(Syntax, ctor, args) { WasCompilerGenerated = true }; + public BoundObjectCreationExpression New(MethodSymbol constructor, ImmutableArray arguments, ImmutableArray argumentRefKinds) + => new BoundObjectCreationExpression( + Syntax, + constructor, + arguments, + argumentNamesOpt: default, + argumentRefKinds, + expanded: false, + argsToParamsOpt: default, + defaultArguments: default, + constantValueOpt: null, + initializerExpressionOpt: null, + constructor.ContainingType) + { WasCompilerGenerated = true }; + public BoundObjectCreationExpression New(WellKnownMember wm, ImmutableArray args) { var ctor = WellKnownMethod(wm); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index cd3aec6946c79..d502a7080b83c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -58,7 +58,7 @@ public LocalFunctionSymbol( ScopeBinder = binder; - binder = binder.WithUnsafeRegionIfNecessary(syntax.Modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(syntax.Modifiers); if (syntax.TypeParameterList != null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs index a1771beb99a70..8cce2057e5200 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs @@ -72,7 +72,7 @@ internal void ReportAsyncParameterErrors(BindingDiagnosticBag diagnostics, Locat } else if (parameter.Type.IsRestrictedType()) { - diagnostics.Add(ErrorCode.ERR_BadSpecialByRefLocal, getLocation(parameter, location), parameter.Type); + diagnostics.Add(ErrorCode.ERR_BadSpecialByRefParameter, getLocation(parameter, location), parameter.Type); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 650b9b0889d13..b8d0c5696f816 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -82,7 +82,7 @@ internal SyntaxReference SyntaxRef } } - internal virtual CSharpSyntaxNode SyntaxNode + internal CSharpSyntaxNode SyntaxNode { get { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 46d4da363c7a8..087dda527e4e1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -63,7 +63,7 @@ private static SourcePropertySymbol Create( bool isAutoProperty = (modifiers & DeclarationModifiers.Partial) == 0 && !accessorsHaveImplementation; bool isExpressionBodied = !hasAccessorList && GetArrowExpression(syntax) != null; - binder = binder.WithUnsafeRegionIfNecessary(modifiersTokenList); + binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiersTokenList); TypeSymbol? explicitInterfaceType; string? aliasQualifierOpt; string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, explicitInterfaceSpecifier, name, diagnostics, out explicitInterfaceType, out aliasQualifierOpt); @@ -473,7 +473,7 @@ private Binder CreateBinderForTypeAndParameters() var binderFactory = compilation.GetBinderFactory(syntaxTree); var binder = binderFactory.GetBinder(syntax, syntax, this); SyntaxTokenList modifiers = GetModifierTokensSyntax(syntax); - binder = binder.WithUnsafeRegionIfNecessary(modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiers); return binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index ecbfda140b18a..11ba0a2c38e55 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -32,6 +32,11 @@ {0}: abstraktní událost nemůže používat syntaxi přístupového objektu události. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' pro skupiny metod se nedá použít ve stromech výrazů. @@ -267,14 +272,14 @@ Člen záznamu {0} musí být čitelná vlastnost instance nebo pole typu {1}, která se bude shodovat s pozičním parametrem {2}. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Příkaz lock pro hodnotu typu System.Threading.Lock nejde použít v asynchronních metodách nebo asynchronních výrazech lambda. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Prostředek použitého příkazu typu {0} nejde použít v asynchronních metodách ani v asynchronních výrazech lambda. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ Typ {0} není platný pro using static. Lze použít pouze třídu, strukturu, rozhraní, výčet, delegáta nebo obor názvů. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Atribut AsyncMethodBuilder je u anonymních metod bez explicitního návratového typu zakázaný. @@ -1812,6 +1822,11 @@ Pole ref lze deklarovat pouze ve struktuře ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Levá strana přiřazení odkazu musí být parametr Ref. @@ -2447,6 +2462,11 @@ parametry ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator uvolněný operátor směny @@ -2622,6 +2642,16 @@ Modifikátor ref pro argument odpovídající parametru in je ekvivalentem in. Místo toho zvažte použití in. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Je nutné zadat parametr atributu SizeConst. @@ -9236,11 +9266,6 @@ Blok catch() po bloku catch (System.Exception e) může zachytit výjimky, kter Parametr ref, out nebo in {0} nejde použít uvnitř anonymní metody, výrazu lambda, výrazu dotazu nebo lokální funkce. - - Unsafe code may not appear in iterators - Iterátory nesmí obsahovat nezabezpečený kód. - - Cannot yield a value in the body of a catch clause V těle klauzule catch nejde použít hodnotu získanou příkazem yield. @@ -10625,8 +10650,8 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např - 'await' cannot be used in an expression containing the type '{0}' - 'Operátor await nejde použít ve výrazu, který obsahuje typ {0}. + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'Operátor await nejde použít ve výrazu, který obsahuje typ {0}. @@ -10694,16 +10719,6 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např Modifikátor async se dá použít jenom v metodách, které mají tělo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parametry nebo lokální proměnné typu {0} nemůžou být deklarované v asynchronních metodách nebo asynchronních výrazech lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Výraz foreach nejde použít na enumerátorech typu {0} v asynchronních metodách nebo metodách iterátoru, protože {0} je struktura REF. - - Security attribute '{0}' cannot be applied to an Async method. Atribut zabezpečení {0} nejde použít pro metodu Async. @@ -11223,7 +11238,7 @@ Potlačení upozornění zvažte jenom v případě, když určitě nechcete če Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Instance typu {0} nelze použít uvnitř vnořené funkce, výrazu dotazu, bloku iterátoru nebo asynchronní metody. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Místní hodnotu odkazu {0} nejde použít uvnitř anonymní metody, výrazu lambda nebo výrazu dotazu. - - Iterators cannot have by-reference locals - Iterátory nemůžou mít lokální proměnné podle odkazu. - - - - Async methods cannot have by-reference locals - Asynchronní metody nemůžou mít lokální proměnné podle odkazu. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Odkaz vrácený voláním funkce {0} nelze zachovat v rámci hranice await nebo yield. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ab12080b9a89f..21d67f2181339 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -32,6 +32,11 @@ {0}: Das abstrakte Ereignis kann die Ereignisaccessorsyntax nicht verwenden. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees "&" für Methodengruppen kann in Ausdrucksbaumstrukturen nicht verwendet werden. @@ -267,14 +272,14 @@ Das Datensatzelement "{0}" muss eine lesbare Instanzeigenschaft oder ein Feld vom Typ "{1}" sein, um dem Positionsparameter "{2}" zu entsprechen. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Eine Sperranweisung für einen Wert vom Typ „System.Threading.Lock“ kann nicht in asynchronen Methoden oder asynchronen Lambdaausdrücken verwendet werden. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Eine using-Anweisungsressource vom Typ „{0}“ kann nicht in asynchronen Methoden oder asynchronen Lambdaausdrücken verwendet werden. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ Der Typ "{0}" ist für "using static" ungültig. Nur eine Klasse, Struktur, Schnittstelle, Enumeration, ein Delegat oder ein Namespace kann verwendet werden. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Das AsyncMethodBuilder-Attribut ist für anonyme Methoden ohne expliziten Rückgabetyp unzulässig. @@ -1812,6 +1822,11 @@ Ein Verweisfeld kann nur in einer Verweisstruktur deklariert werden. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Die linke Seite einer Ref-Zuweisung muss eine Ref-Variable sein. @@ -2447,6 +2462,11 @@ ref readonly-Parameter + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator entspannter Schichtoperator @@ -2622,6 +2642,16 @@ Der ref-Modifizierer für ein Argument, das dem in-Parameter entspricht, entspricht "in". Erwägen Sie stattdessen die Verwendung von "in". + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Der Attributparameter "SizeConst" muss angegeben werden. @@ -9236,11 +9266,6 @@ Ein catch()-Block nach einem catch (System.Exception e)-Block kann nicht-CLS-Aus Der ref-, out-, oder in-Parameter "{0}" kann nicht in einer anonymen Methode, einem Lambdaausdruck, einem Abfrageausdruck oder einer lokalen Funktion verwendet werden. - - Unsafe code may not appear in iterators - Unsicherer Code wird möglicherweise nicht in Iteratoren angezeigt. - - Cannot yield a value in the body of a catch clause Mit "yield" kann im Text einer catch-Klausel kein Wert zurückgegeben werden. @@ -10625,8 +10650,8 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz - 'await' cannot be used in an expression containing the type '{0}' - '"await" kann nicht in einem Ausdruck verwendet werden, der den Typ "{0}" enthält + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '"await" kann nicht in einem Ausdruck verwendet werden, der den Typ "{0}" enthält @@ -10694,16 +10719,6 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz Der Modifizierer "async" kann nur in Methoden verwendet werden, die über einen Textkörper verfügen. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parameter oder lokale Variablen des Typs "{0}" können nicht in asynchronen Methoden oder in asynchronen Lambdaausdrücken deklariert werden. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Die foreach-Anweisung kann nicht für Enumeratoren vom Typ "{0}" in asynchronen oder Iteratormethoden verwendet werden, weil "{0}" eine Referenzstruktur ist. - - Security attribute '{0}' cannot be applied to an Async method. Das Sicherheitsattribut "{0}" kann nicht auf eine Async-Methode angewendet werden. @@ -11223,7 +11238,7 @@ Sie sollten das Unterdrücken der Warnung nur in Betracht ziehen, wenn Sie siche Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Eine Instanz des Typs "{0}" kann nicht in einer geschachtelten Funktion, einem Abfrageausdruck, einem Iteratorblock oder einer Async-Methode verwendet werden. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Der lokale Verweis "{0}" kann nicht in einer anonymen Methode, einem Lambdaausdruck oder einem Abfrageausdruck verwendet werden. - - Iterators cannot have by-reference locals - Iteratoren dürfen keine lokalen by-reference-Elemente aufweisen. - - - - Async methods cannot have by-reference locals - Asynchrone Methoden dürfen keine lokalen by-reference-Elemente aufweisen. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Ein Verweis, der von einem Aufruf von "{0}" zurückgegeben wird, kann nicht über die Grenzen "await" oder "yield" hinweg beibehalten werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 095195dff5c6a..9923e5d425409 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -32,6 +32,11 @@ "{0}": un evento abstracto no puede usar la sintaxis de descriptor de acceso de eventos + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees No se puede usar "&" para los grupos de métodos en los árboles de expresión. @@ -267,14 +272,14 @@ El miembro de registro '{0}' debe ser una propiedad de instancia legible o un campo de tipo '{1}' para coincidir con el parámetro posicional '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - No se puede usar una instrucción de bloqueo en un valor de tipo 'System.Threading.Lock' en métodos asincrónicos ni expresiones lambda asincrónicas. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Un recurso de instrucción using de tipo '{0}' no se puede usar en métodos asincrónicos ni expresiones lambda asincrónicas. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ El tipo '{0}' no es válido para 'using static'. Solo se puede usar una clase, estructura, interfaz, enumeración, delegado o espacio de nombres. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. El atributo AsyncMethodBuilder no se permite en métodos anónimos sin un tipo de valor devuelto explícito. @@ -1812,6 +1822,11 @@ Un campo ref solo se puede declarar en una estructura ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. La parte izquierda de una asignación de referencias debe ser una variable local. @@ -2447,6 +2462,11 @@ parámetros ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operador de cambios relajado @@ -2622,6 +2642,16 @@ El modificador "ref" de un argumento correspondiente al parámetro "in" es equivalente a "in". Considere la posibilidad de usar "in" en su lugar. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Se debe especificar el parámetro de atributo "SizeConst". @@ -9236,11 +9266,6 @@ Un bloque catch() después de un bloque catch (System.Exception e) puede abarcar No se puede usar el parámetro ref, out o in "{0}" dentro de un método anónimo, una expresión lambda, una expresión de consulta o una función local - - Unsafe code may not appear in iterators - No puede aparecer código no seguro en iteradores - - Cannot yield a value in the body of a catch clause No se puede proporcionar ningún valor en el cuerpo de una cláusula catch @@ -10625,8 +10650,8 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue - 'await' cannot be used in an expression containing the type '{0}' - 'await' no se puede usar en una expresión que contenga el tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' no se puede usar en una expresión que contenga el tipo '{0}' @@ -10694,16 +10719,6 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue El modificador 'async' solo se puede usar en métodos que tengan un cuerpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Los parámetros o locales de tipo '{0}' no pueden declararse en expresiones lambda o métodos asincrónicos. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - La instrucción foreach no puede funcionar en enumeradores de tipo "{0}" en métodos async o iterator porque "{0}" es una estructura ref. - - Security attribute '{0}' cannot be applied to an Async method. El atributo de seguridad '{0}' no se puede aplicar a un método Async. @@ -11223,7 +11238,7 @@ Considere la posibilidad de suprimir la advertencia solo si tiene la seguridad d Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - La instancia de tipo "{0}" no se puede usar dentro de una función anidada, una expresión de consulta, un bloque iterador ni un método asincrónico. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe No se puede usar la variable local de tipo ref '{0}' dentro de un método anónimo, una expresión lambda o una expresión de consulta. - - Iterators cannot have by-reference locals - Los iteradores no pueden tener variables locales por referencia. - - - - Async methods cannot have by-reference locals - Los métodos asincrónicos no pueden tener variables locales por referencia. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Una referencia devuelta por una llamada a '{0}' no se puede conservar a través del límite "await" o "yield". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 9844403cce8d7..891c7f1ac89cb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -32,6 +32,11 @@ '{0}' : un événement abstrait ne peut pas utiliser une syntaxe d'accesseur d'événement + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' des groupes de méthodes ne peut pas être utilisé dans les arborescences d'expression @@ -267,14 +272,14 @@ Le membre d'enregistrement '{0}' doit être une propriété d'instance our champ lisible de type '{1}' pour correspondre au paramètre positionnel '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Vous ne pouvez pas utiliser une instruction lock sur une valeur de type « System.Threading.Lock » dans des méthodes asynchrones ou des expressions lambda asynchrones. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Une ressource d’instruction d’utilisation de type '{0}' ne peut pas être utilisée dans des méthodes asynchrones ou des expressions lambda asynchrones. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ Le type '{0}' n'est pas valide pour 'en utilisant statique'. Seuls une classe, une structure, une interface, une énumération, un délégué ou un espace de noms peuvent être utilisés. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. L'attribut AsyncMethodBuilder n'est pas autorisé pour les méthodes anonymes sans type de retour explicite. @@ -1812,6 +1822,11 @@ Un champ de référence ne peut être déclaré que dans une sructure de référence. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Le côté gauche d’une affectation ref doit être une variable ref. @@ -2447,6 +2462,11 @@ paramètres ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator opérateur shift souple @@ -2622,6 +2642,16 @@ Le modificateur « ref » d’un argument correspondant au paramètre « in » est équivalent à « in ». Envisagez d’utiliser « in » à la place. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Vous devez spécifier un paramètre d’attribut « SizeConst ». @@ -9236,11 +9266,6 @@ Un bloc catch() après un bloc catch (System.Exception e) peut intercepter des e Impossible d'utiliser le paramètre ref, out ou in '{0}' dans une méthode anonyme, une expression lambda, une expression de requête ou une fonction locale - - Unsafe code may not appear in iterators - Du code unsafe ne peut pas s'afficher dans des itérateurs - - Cannot yield a value in the body of a catch clause Impossible de générer une valeur dans le corps d'une clause catch @@ -10625,8 +10650,8 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve - 'await' cannot be used in an expression containing the type '{0}' - 'await' ne peut pas être utilisé dans une expression contenant le type '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' ne peut pas être utilisé dans une expression contenant le type '{0}' @@ -10694,16 +10719,6 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve Le modificateur 'async' ne peut être utilisé que dans des méthodes ayant un corps. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Les paramètres ou variables locales de type '{0}' ne peuvent pas être déclarés dans des méthodes asynchrones ou des expressions asynchrones lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - L'instruction foreach ne peut pas fonctionner sur les énumérateurs de type '{0}' dans les méthodes asynchrones ou les méthodes d'itérateurs, car '{0}' est un struct par référence. - - Security attribute '{0}' cannot be applied to an Async method. Impossible d'appliquer l'attribut de sécurité '{0}' à une méthode Async. @@ -11223,7 +11238,7 @@ Supprimez l'avertissement seulement si vous êtes sûr de ne pas vouloir attendr Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Impossible d'utiliser une instance de type '{0}' dans une fonction imbriquée, une expression de requête, un bloc itérateur ou une méthode async + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Impossible d'utiliser ref local '{0}' dans une méthode anonyme, une expression lambda ou une expression de requête - - Iterators cannot have by-reference locals - Les itérateurs ne peuvent pas avoir de variables locales par référence - - - - Async methods cannot have by-reference locals - Les méthodes async ne peuvent pas avoir de variables locales par référence - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Une référence renvoyée par un appel à '{0}' ne peut pas être conservée à travers la limite 'wait' ou 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index b84cc35273825..4d43c88123126 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -32,6 +32,11 @@ '{0}': l'evento astratto non può usare la sintassi della funzione di accesso agli eventi + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Non è possibile usare '&' su gruppi di metodi in alberi delle espressioni @@ -267,14 +272,14 @@ Il membro di record '{0}' deve essere una proprietà di istanza leggibile o campo di tipo '{1}' per corrispondere al parametro posizionale '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Non è possibile usare un'istruzione lock in un valore di tipo 'System.Threading.Lock' nei metodi asincroni o nelle espressioni lambda asincrone. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Non è possibile usare una risorsa di istruzione using di tipo '{0}' in metodi asincroni o espressioni lambda asincrone. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' tipo non valido per 'using static'. È possibile usare solo una classe, una struttura, un'interfaccia, un'enumerazione, un delegato o uno spazio dei nomi. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. L'attributo AsyncMethodBuilder non è consentito in metodi anonimi senza un tipo restituito esplicito. @@ -1812,6 +1822,11 @@ Un campo ref può essere dichiarato solo in uno struct ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. La parte sinistra di un'assegnazione ref deve essere una variabile ref. @@ -2447,6 +2462,11 @@ parametri di sola lettura ref + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operatore di spostamento rilassato @@ -2622,6 +2642,16 @@ Il modificatore 'ref' per un argomento corrispondente al parametro 'in' equivale a 'in'. Provare a usare 'in'. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. È necessario specificare il parametro di attributo 'SizeConst'. @@ -9236,11 +9266,6 @@ Un blocco catch() dopo un blocco catch (System.Exception e) può rilevare eccezi Non è possibile usare il parametro ref, out o in '{0}' all'interno di un metodo anonimo, di un'espressione lambda, di un'espressione di query o di una funzione locale - - Unsafe code may not appear in iterators - Gli iteratori non possono contenere codice unsafe - - Cannot yield a value in the body of a catch clause Impossibile produrre un valore nel corpo di una clausola catch @@ -10625,8 +10650,8 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn - 'await' cannot be used in an expression containing the type '{0}' - 'non è possibile usare 'await' in un'espressione contenente il tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'non è possibile usare 'await' in un'espressione contenente il tipo '{0}' @@ -10694,16 +10719,6 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn Il modificatore 'async' può essere usato solo nei metodi con un corpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Non è possibile dichiarare parametri o variabili locali di tipo '{0}' in metodi asincroni o espressioni lambda asincrone. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - L'istruzione foreach non può funzionare con enumeratori di tipo '{0}' in metodi async o iterator perché '{0}' è uno struct ref. - - Security attribute '{0}' cannot be applied to an Async method. Non è possibile applicare l'attributo di sicurezza '{0}' a un metodo Async @@ -11223,7 +11238,7 @@ Come procedura consigliata, è consigliabile attendere sempre la chiamata. Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - L'istanza di tipo '{0}' non può essere usata all'interno di una funzione annidata, un'espressione di query, un blocco iteratore o un metodo asincrono + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Non è possibile usare la variabile locale ref '{0}' in un metodo anonimo, in un'espressione lambda o in un'espressione di query - - Iterators cannot have by-reference locals - Gli iteratori non possono includere variabili locali per riferimento - - - - Async methods cannot have by-reference locals - I metodi Async non possono includere variabili locali per riferimento - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Non è possibile mantenere un riferimento restituito da una chiamata a '{0}' oltre il limite 'await' o 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 14766847b5c44..c5024b07f74b4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -32,6 +32,11 @@ '{0}': 抽象イベントはイベント アクセサーの構文を使用できません + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees メソッド グループの '&' を式ツリーで使用することはできません @@ -267,14 +272,14 @@ レコード メンバー '{0}' は、位置指定パラメーター '{2}' に一致させるための型 '{1}' の読み取り可能なインスタンス プロパティまたはフィールドである必要があります。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - 型 'System.Threading.Lock' の値に対する lock ステートメントは、非同期メソッドまたは非同期ラムダ式では使用できません。 + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 型 '{0}' の using ステートメント リソースは、非同期メソッドまたは非同期ラムダ式では使用できません。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' 型は 'using static' では無効です。使用できるのは、クラス、構造体、インターフェイス、列挙型、デリゲート、名前空間のみです。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. AsyncMethodBuilder 属性は、明示的な戻り値の型のない匿名メソッドでは許可されていません。 @@ -1812,6 +1822,11 @@ ref フィールドは ref 構造体でのみ宣言できます。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 代入の左辺は ref 変数である必要があります。 @@ -2447,6 +2462,11 @@ ref readonly パラメーター + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 緩和されたシフト演算子 @@ -2622,6 +2642,16 @@ 'in' パラメーターに対応する引数の 'ref' 修飾子は 'in' と同じです。代わりに 'in' を使用することを検討してください。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 属性パラメーター 'SizeConst' を指定する必要があります。 @@ -9236,11 +9266,6 @@ AssemblyInfo.cs ファイルで RuntimeCompatibilityAttribute が false に設 ref、out、in パラメーター '{0}' は、匿名メソッド、ラムダ式、クエリ式、ローカル関数の内部では使用できません - - Unsafe code may not appear in iterators - アンセーフ コードは反復子には記述できません - - Cannot yield a value in the body of a catch clause catch 句の本体で値を生成することはできません @@ -10625,8 +10650,8 @@ C# では out と ref を区別しますが、CLR では同じと認識します - 'await' cannot be used in an expression containing the type '{0}' - 'await' は、型 '{0}' を含む式では使用できません + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' は、型 '{0}' を含む式では使用できません @@ -10694,16 +10719,6 @@ C# では out と ref を区別しますが、CLR では同じと認識します async' 修飾子は、本体があるメソッドでのみ使用できます。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - '{0}' 型のパラメーターまたはローカルは、非同期メソッドまたは非同期ラムダ式で宣言することができません。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}' は ref 構造体であるため、非同期または反復子のメソッド内で型 '{0}' の列挙子に対して foreach ステートメントは機能しません。 - - Security attribute '{0}' cannot be applied to an Async method. セキュリティ属性 '{0}' を非同期メソッドに適用することはできません。 @@ -11223,7 +11238,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - 型 '{0}' のインスタンスは、入れ子になった関数、クエリ式、反復子ブロック、または非同期メソッドの中では使用できません + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 匿名メソッド、ラムダ式、クエリ式内で ref ローカル変数 '{0}' は使用できません - - Iterators cannot have by-reference locals - 反復子は参照渡しのローカル変数を持つことができません - - - - Async methods cannot have by-reference locals - 非同期メソッドは参照渡しのローカル変数を持つことができません - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}' への呼び出しによって返された参照は、'await' または 'yield' 境界を越えて保持することはできません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 0938f6693eace..6e03cd1b5c017 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -32,6 +32,11 @@ '{0}': 추상 이벤트는 이벤트 접근자 구문을 사용할 수 없습니다. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 식 트리에서는 메서드 그룹에 '&'를 사용할 수 없습니다. @@ -267,14 +272,14 @@ 위치 매개 변수 '{0}'과(와) 일치하려면 레코드 멤버 '{1}'이(가) 유형 '{2}'의 읽을 수 있는 인스턴스 속성 또는 필드여야 합니다. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - 'System.Threading.Lock' 형식의 값에 대한 lock 문은 비동기 메서드 또는 비동기 람다 식에서 사용할 수 없습니다. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - '{0}' 형식의 using 문 리소스는 비동기 메서드 또는 비동기 람다 식에 사용할 수 없습니다. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' 유형은 '정적 사용'에 유효하지 않습니다. 클래스, 구조체, 인터페이스, 열거형, 대리자 또는 네임스페이스만 사용할 수 있습니다. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. AsyncMethodBuilder 특성은 명시적 반환 형식이 없는 익명 메서드에서 허용되지 않습니다. @@ -1812,6 +1822,11 @@ ref 필드는 ref 구조체에서만 선언할 수 있습니다. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 할당의 왼쪽은 ref 변수여야 합니다. @@ -2447,6 +2462,11 @@ ref readonly 매개 변수 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 완화된 시프트 연산자 @@ -2622,6 +2642,16 @@ 'in' 매개 변수에 해당하는 인수의 'ref' 한정자는 'in'에 해당합니다. 대신 'in'을 사용하는 것이 좋습니다. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 특성 매개 변수 'SizeConst'를 지정해야 합니다. @@ -9236,11 +9266,6 @@ catch (System.Exception e) 블록 뒤의 catch() 블록은 RuntimeCompatibilityA 무명 메서드, 람다 식, 쿼리 식 또는 로컬 함수 안에서는 ref, out 또는 in 매개 변수 '{0}'을(를) 사용할 수 없습니다. - - Unsafe code may not appear in iterators - 반복기에는 안전하지 않은 코드를 사용할 수 없습니다. - - Cannot yield a value in the body of a catch clause catch 절 본문에서는 값을 생성할 수 없습니다. @@ -10625,8 +10650,8 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 - 'await' cannot be used in an expression containing the type '{0}' - 'await'는 '{0}' 형식이 포함된 식에 사용할 수 없습니다. + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await'는 '{0}' 형식이 포함된 식에 사용할 수 없습니다. @@ -10694,16 +10719,6 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 async' 한정자는 본문이 있는 메서드에서만 사용할 수 있습니다. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - '{0}' 형식의 매개 변수 또는 로컬은 비동기 메서드나 비동기 람다 식에서 선언할 수 없습니다. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}'은(는) ref struct이므로 비동기 또는 반복기 메서드의 '{0}' 형식 열거자에서 foreach 문을 수행할 수 없습니다. - - Security attribute '{0}' cannot be applied to an Async method. '{0}' 보안 특성은 비동기 메서드에 적용할 수 없습니다. @@ -11223,7 +11238,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - '{0}' 형식의 인스턴스는 중첩된 함수, 쿼리 식, 반복기 블록 또는 비동기 메서드 내에서 사용할 수 없습니다. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 무명 메서드, 람다 식 또는 쿼리 식에는 참조 로컬 '{0}'을(를) 사용할 수 없습니다. - - Iterators cannot have by-reference locals - 반복기에 by-reference 로컬을 사용할 수 없습니다. - - - - Async methods cannot have by-reference locals - 비동기 메서드에 by-reference 로컬을 사용할 수 없습니다. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}'에 대한 호출로 반환된 참조는 'await' 또는 'yield' 경계에서 보존할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 86c1af3d52b55..de8228cf881ad 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -32,6 +32,11 @@ „{0}”: zdarzenie abstrakcyjne nie może używać składni metody dostępu zdarzenia + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Znak „&” dla grup metod nie może być używany w drzewach wyrażeń @@ -267,14 +272,14 @@ Składowa rekordu "{0}" musi być możliwą do odczytu właściwością wystąpienia typu "{1}", aby dopasować parametr pozycyjny "{2}". - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Instrukcja blokady na wartości typu „System.Threading.Lock” nie może być używana w metodach asynchronicznych lub asynchronicznych wyrażeniach lambda. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Zasobu instrukcji przy użyciu typu '{0}' nie można używać w metodach asynchronicznych ani asynchronicznych wyrażeniach lambda. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ Typ „{0}” jest nieprawidłowy dla „using static”. Można używać tylko klasy, struktury, interfejsu, wyliczenia, delegata lub przestrzeni nazw. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Atrybut AsyncMethodBuilder jest niedozwolony w metodach anonimowych bez jawnego zwracanego typu. @@ -1812,6 +1822,11 @@ Pole referencyjne można zadeklarować tylko w strukturze referencyjnej. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Lewa strona przypisania referencyjnego musi być zmienną referencyjną. @@ -2447,6 +2462,11 @@ parametry tylko do odczytu ref + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operator swobodnej zmiany @@ -2622,6 +2642,16 @@ Modyfikator „ref” dla argumentu odpowiadającego parametrowi „in” jest równoważny parametrowi „in”. Zamiast tego rozważ użycie parametru „in”. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Należy określić parametr atrybutu „SizeConst”. @@ -9236,11 +9266,6 @@ Blok catch() po bloku catch (System.Exception e) może przechwytywać wyjątki n Nie można użyć parametru ref, out ani in „{0}” wewnątrz metody anonimowej, wyrażenia lambda, wyrażenia zapytania lub funkcji lokalnej - - Unsafe code may not appear in iterators - Niebezpieczny kod nie może występować w iteratorach. - - Cannot yield a value in the body of a catch clause Nie można użyć instrukcji yield z wartością w treści klauzuli catch. @@ -10625,8 +10650,8 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada - 'await' cannot be used in an expression containing the type '{0}' - 'Operatora „await” nie można użyć w wyrażeniu zawierającym typ „{0}” + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'Operatora „await” nie można użyć w wyrażeniu zawierającym typ „{0}” @@ -10694,16 +10719,6 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada Modyfikatora „async” można używać tylko w metodach mających treść. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parametrów ani elementów lokalnych typu „{0}” nie można deklarować w metodach asynchronicznych ani wyrażeniach lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Instrukcja foreach nie może działać na modułach wyliczających typu „{0}” w metodach asynchronicznych lub iteratora, ponieważ element „{0}” jest strukturą ref. - - Security attribute '{0}' cannot be applied to an Async method. Atrybutu zabezpieczeń „{0}” nie można zastosować dla metody asynchronicznej. @@ -11223,7 +11238,7 @@ Pominięcie ostrzeżenia należy wziąć pod uwagę tylko w sytuacji, gdy na pew Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Wystąpienia typu „{0}” nie można użyć wewnątrz funkcji zagnieżdżonej, wyrażenia zapytania, bloku iteratora ani metody asynchronicznej + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Nie można użyć zmiennej lokalnej typu ref „{0}” wewnątrz metody anonimowej, wyrażenia lambda ani wyrażenia zapytania - - Iterators cannot have by-reference locals - Iteratory nie mogą mieć zmiennych lokalnych dostępnych przez odwołanie - - - - Async methods cannot have by-reference locals - Metody asynchroniczne nie mogą mieć zmiennych lokalnych dostępnych przez odwołanie - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Odwołanie zwrócone przez wywołanie „{0}” nie może zostać zachowane w granicach „await” lub „yield”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 3036cc562544e..06f8d4b504fa4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -32,6 +32,11 @@ '{0}': o evento abstrato não pode usar a sintaxe do acessador de eventos + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' nos grupos de métodos não pode ser usado em árvores de expressão @@ -267,14 +272,14 @@ O membro do registro '{0}' precisa ser uma propriedade de instância legível ou campo do tipo '{1}' para corresponder ao parâmetro posicional '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Uma instrução lock em um valor do tipo "System.Threading.Lock" não pode ser usada em métodos assíncronos ou em expressões lambda assíncronas. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Um recurso de instrução using do tipo "{0}" não pode ser usado em métodos assíncronos ou expressões lambda assíncronas. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' tipo não é válido para 'using static'. Somente uma classe, struct, interface, enumeração, delegado ou namespace podem ser usados. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. O atributo AsyncMethodBuilder não é permitido em métodos anônimos sem um tipo de retorno explícito. @@ -1812,6 +1822,11 @@ Um campo ref só pode ser declarado em uma estrutura ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. O lado esquerdo de uma atribuição ref deve ser uma variável ref. @@ -2447,6 +2462,11 @@ parâmetros ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operador de deslocamento flexível @@ -2622,6 +2642,16 @@ O modificador 'ref' do argumento correspondente ao parâmetro 'in' é equivalente a 'in'. Considere usar 'in' em vez disso. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. O parâmetro de atribuição 'SizeConst' deve ser especificado. @@ -9236,11 +9266,6 @@ Um bloco catch() depois de um bloco catch (System.Exception e) poderá capturar Não é possível usar os parâmetro ref, out ou in '{0}' dentro de um método anônimo, de uma expressão lambda de uma expressão de consulta ou de uma função local - - Unsafe code may not appear in iterators - Código sem segurança só pode aparecer em iteradores - - Cannot yield a value in the body of a catch clause Não é possível usar a instrução yield no corpo de uma cláusula catch @@ -10625,8 +10650,8 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc - 'await' cannot be used in an expression containing the type '{0}' - 'aguardar' não pode ser usado em uma expressão que contém o tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'aguardar' não pode ser usado em uma expressão que contém o tipo '{0}' @@ -10694,16 +10719,6 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc O modificador 'async' só pode ser usado em métodos que têm um corpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Os parâmetros ou locais do tipo '{0}' não podem ser declarados nos métodos async ou expressões async lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - a instrução foreach não pode operar em enumeradores do tipo '{0}' em métodos assíncronos ou iteradores porque '{0}' é uma struct de referência. - - Security attribute '{0}' cannot be applied to an Async method. Atributo de segurança "{0}" não pode ser aplicado a um método Assíncrono. @@ -11223,7 +11238,7 @@ Você pode suprimir o aviso se tiver certeza de que não vai querer aguardar a c Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - A instância do tipo '{0}' não pode ser usada dentro de uma função aninhada, expressão de consulta, bloco de iteradores ou método assíncrono + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Não é possível usar a referência local '{0}' em um método anônimo, expressão lambda ou expressão de consulta - - Iterators cannot have by-reference locals - Os iteradores não podem ter locais por referência - - - - Async methods cannot have by-reference locals - Os métodos assíncronos não podem ter locais por referência - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Uma referência retornada por uma chamada para '{0}' não pode ser preservada no limite 'await' ou 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 08c58fdd199e3..548cca9db9083 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -32,6 +32,11 @@ "{0}": абстрактное событие не может использовать синтаксис метода доступа к событиям. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees "&" в группах методов не может использоваться в деревьях выражений @@ -267,14 +272,14 @@ Элемент записи "{0}" должен быть доступным для чтения свойством экземпляра или полем типа "{1}", чтобы соответствовать позиционному параметру "{2}". - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Оператор блокировки над значением типа "System.Threading.Lock" нельзя использовать в асинхронных методах и в асинхронных лямбда-выражениях. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Ресурс оператора использования типа "{0}" нельзя применять в асинхронных методах или асинхронных лямбда-выражениях. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ ' {0} ' недопустим для 'использования статики'. Можно использовать только класс, структуру, интерфейс, перечисление, делегат или пространство имен. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Атрибут AsyncMethodBuilder запрещен для анонимных методов без явного типа возвращаемого значения. @@ -1812,6 +1822,11 @@ Поле ссылки может быть объявлено только в структуре ссылки. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Левая сторона назначения ref должна быть переменной ref. @@ -2447,6 +2462,11 @@ Параметры ref, доступные только для чтения + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator нестрогий оператор сдвига @@ -2622,6 +2642,16 @@ Модификатор "ref" для аргумента, соответствующего параметру "in", эквивалентен "in". Попробуйте вместо этого использовать "in". + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Должен быть указан параметр атрибута "SizeConst". @@ -9237,11 +9267,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Недопустимо использовать параметр "{0}" с модификаторами ref, out или in внутри анонимного метода, лямбда-выражения, выражения запроса или локальной функции - - Unsafe code may not appear in iterators - Небезопасный код не может использоваться в итераторах. - - Cannot yield a value in the body of a catch clause Нельзя использовать оператор yield в теле предложения catch. @@ -10626,8 +10651,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - '"await" нельзя использовать в выражении, содержащем тип "{0}" + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '"await" нельзя использовать в выражении, содержащем тип "{0}" @@ -10695,16 +10720,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi Модификатор "async" можно использовать только в методах, имеющих тело. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Параметры или локальные переменные типа "{0}" не могут объявляться в асинхронных методах и в асинхронных лямбда-выражениях. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Оператор foreach нельзя использовать с перечислителями типа "{0}" в методах с модификатором Async или Iterator, так как "{0}" является ссылочной структурой. - - Security attribute '{0}' cannot be applied to an Async method. Атрибут безопасности "{0}" нельзя применить к асинхронному методу. @@ -11224,7 +11239,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Экземпляр типа "{0}" нельзя использовать внутри вложенной функции, выражения запроса, блока итератора или асинхронного метода. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12535,16 +12550,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Невозможно использовать локальную переменную ref "{0}" внутри анонимного метода, лямбда-выражения или выражения запроса - - Iterators cannot have by-reference locals - Итераторы не могут иметь локальных переменных по ссылке - - - - Async methods cannot have by-reference locals - Асинхронные методы не могут иметь локальных переменных по ссылке - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Ссылка, возвращенная вызовом ' {0} ', не может быть сохранена за границей 'wait' или 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index dee4c9d559813..14b93604f8502 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -32,6 +32,11 @@ '{0}': soyut olay, olay erişeni söz dizimini kullanamaz + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Metot gruplarındaki '&', ifade ağaçlarında kullanılamaz @@ -267,14 +272,14 @@ {0} kayıt üyesi, {1} konumsal parametresi ile eşleşmesi için {2} türünde okunabilir bir örnek özelliği veya alan olmalıdır. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - 'System.Threading.Lock' türündeki bir değere ilişkin lock deyimi, asenkron yöntemlerde veya asenkron lambda ifadelerinde kullanılamaz. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - '{0}' türündeki bir using deyimi kaynağı, asenkron yöntemlerde veya asenkron lambda ifadelerinde kullanılamaz. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' türü 'using static' için geçerli değil. Yalnızca bir sınıf, yapı, arabirim, sabit liste, temsilci veya ad alanı kullanılabilir. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Açık dönüş türü olmadan, anonim yöntemlerde AsyncMethodBuilder özniteliğine izin verilmez. @@ -1812,6 +1822,11 @@ Başvuru alanı yalnızca başvuru yapısında bildirilebilir. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref atamasının sol tarafı, ref değişkeni olmalıdır. @@ -2447,6 +2462,11 @@ ref salt okunur parametreleri + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator esnek kaydırma işleci @@ -2622,6 +2642,16 @@ 'in' parametresine karşılık gelen bir bağımsız değişken için 'ref' değiştiricisi 'in' ile eşdeğerdir. Bunun yerine 'in' kullanmayı düşünün. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 'SizeConst' öznitelik parametresi belirtilmelidir. @@ -9236,11 +9266,6 @@ RuntimeCompatibilityAttribute AssemblyInfo.cs dosyasında false olarak ayarlanm Anonim metot, lambda ifadesi, sorgu ifadesi veya yerel işlev içinde '{0}' ref, out veya in parametresi kullanılamaz - - Unsafe code may not appear in iterators - Güvenli olmayan kod yineleyicilerde görünmeyebilir - - Cannot yield a value in the body of a catch clause Catch yan tümcesinin gövdesinde yield ile bir değer döndürülemez @@ -10625,8 +10650,8 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl - 'await' cannot be used in an expression containing the type '{0}' - 'await', '{0}' türünü içeren bir ifadede kullanılamaz + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await', '{0}' türünü içeren bir ifadede kullanılamaz @@ -10694,16 +10719,6 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl Async' değiştiricisi yalnızca gövdesi olan metotlarda kullanılabilir. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Zaman uyumsuz yöntemlerde veya zaman uyumsuz lambda ifadelerinde '{0}' türündeki parametreler veya yerel öğeler bildirilemez. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}' bir başvuru yapısı olduğundan foreach deyimi, async veya iterator metotlarındaki '{0}' türü numaralandırıcılar üzerinde çalışamaz. - - Security attribute '{0}' cannot be applied to an Async method. '{0}' güvenlik özniteliği bir Async yöntemine uygulanamaz. @@ -11223,7 +11238,7 @@ Yalnızca asenkron çağrının tamamlanmasını beklemek istemediğinizden ve Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - '{0}' türünün örneği iç içe geçmiş bir işlevde, sorgu ifadesinde, yineleyici bloğunda veya zaman uyumsuz bir metotta kullanılamaz + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T '{0}' ref yerel değeri bir anonim metotta, lambda ifadesinde veya sorgu ifadesinde kullanılamaz - - Iterators cannot have by-reference locals - Yineleyiciler başvuruya göre yerel değerlere sahip olamaz - - - - Async methods cannot have by-reference locals - Zaman uyumsuz metotlar başvuruya göre yerel değerlere sahip olamaz - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}' hedefine bir çağrı tarafından döndürülen başvuru, 'await' veya 'yield' sınırında korunamıyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index a64e41fa3bede..5f517c3edecbb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -32,6 +32,11 @@ “{0}”: 抽象事件不可使用事件访问器语法 + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 不可在表达式树中使用方法组上的 "&" @@ -267,14 +272,14 @@ 记录成员 '{0}' 必须为类型 '{1}' 的可读实例属性或字段,以匹配位置参数 '{2}'。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - 类型“System.Threading.Lock”的值的 lock 语句不能用于异步方法或异步 lambda 表达式。 + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 无法在异步方法或异步 lambda 表达式中使用类型为“{0}”的 using 语句资源。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ “{0}”类型对于 "using static" 无效。只能使用类、结构、接口、枚举、委托或命名空间。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. 没有显式返回类型的匿名方法不允许使用 AsyncMethodBuilder 属性。 @@ -1812,6 +1822,11 @@ ref 字段只能在 ref 结构中声明。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 赋值的左侧必须为 ref 变量。 @@ -2447,6 +2462,11 @@ ref readonly 参数 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 移位运算符 @@ -2622,6 +2642,16 @@ 与 “in” 参数对应的参数的 “ref” 修饰符等效于 “in”。请考虑改用 “in”。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 必须指定属性参数 “SizeConst”。 @@ -9236,11 +9266,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep 不能在匿名方法、lambda 表达式、查询表达式或本地函数中使用 ref、out 或 in 参数“{0}” - - Unsafe code may not appear in iterators - 迭代器中不能出现不安全的代码 - - Cannot yield a value in the body of a catch clause 无法在 catch 子句体中生成值 @@ -10625,8 +10650,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - '“等待”不能在包含“{0}”类型的表达式中使用 + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '“等待”不能在包含“{0}”类型的表达式中使用 @@ -10694,16 +10719,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi 只能在具有正文的方法中使用 "async" 修饰符。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - 不能在异步方法或异步 lambda 表达式中声明类型“{0}”的参数或局部变量。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - foreach 语句无法在类型“{0}”的枚举器上使用异步或迭代器方法操作,因为“{0}”是 ref 结构。 - - Security attribute '{0}' cannot be applied to an Async method. 安全特性“{0}”不可应用于异步方法。 @@ -11223,7 +11238,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - “{0}”类型的实例不能在嵌套函数、查询表达式、迭代器块或异步方法中使用 + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 不能在匿名方法、lambda 表达式或查询表达式内使用 ref 局部变量“{0}” - - Iterators cannot have by-reference locals - 迭代器不能有按引用局部变量 - - - - Async methods cannot have by-reference locals - 异步方法不能有按引用局部变量 - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. 调用“{0}”所返回的引用不能跨 "await" 或 "yield" 边界保留。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index bcadc22429017..d533b6f0c1f64 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -32,6 +32,11 @@ '{0}' 抽象事件無法使用事件存取子語法 + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 不得在運算式樹狀架構中對方法群組使用 '&' @@ -267,14 +272,14 @@ 記錄成員 '{0}' 必須是類型 '{1}' 的可讀取執行個體屬性或欄位,才能符合位置參數 '{2}'。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - 型別 'System.Threading.Lock' 的值上的 lock 陳述式不能用在非同步方法或非同步 Lambda 運算式中。 + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 類型 '{0}' 的 using 陳述式資源不能用於非同步方法或非同步 Lambda 運算式。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -302,6 +307,11 @@ '{0}' 類型對 'using static' 無效。只能使用類別、結構、介面、列舉、委派或命名空間。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. 沒有明確傳回型別的匿名方法上不允許 AsyncMethodBuilder 屬性。 @@ -1812,6 +1822,11 @@ ref 欄位只能在 ref 結構中宣告。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. 參考指派的左側必須為 ref 變數。 @@ -2447,6 +2462,11 @@ ref readonly 參數 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 寬鬆移位 (Shift) 運算子 @@ -2622,6 +2642,16 @@ 對應至 'in' 參數之引數的 'ref' 修飾元相當於 'in'。請考慮改為使用 'in'。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 必須指定屬性參數 'SizeConst'。 @@ -9236,11 +9266,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep 無法在匿名方法、Lambda 運算式、查詢運算式或區域函式中使用 ref、out 或 in 參數 '{0}' - - Unsafe code may not appear in iterators - Unsafe 程式碼不可出現在迭代器中 - - Cannot yield a value in the body of a catch clause 無法在 catch 子句主體中使用 yield 產生值 @@ -10625,8 +10650,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - 'await' 不得用於包含類型 '{0}' 的運算式中 + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' 不得用於包含類型 '{0}' 的運算式中 @@ -10694,16 +10719,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi async' 修飾元只可用於具有主體的方法。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - 類型 '{0}' 的參數或區域變數,不可在非同步方法或非同步 Lambda 運算式中宣告。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - foreach 陳述式無法對 async 或 iterator 方法中類型 '{0}' 的列舉值進行操作,因為 '{0}' 為 ref struct。 - - Security attribute '{0}' cannot be applied to an Async method. 安全屬性 '{0}' 無法套用至非同步方法。 @@ -11223,7 +11238,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - 類型 '{0}' 的執行個體不可用於巢狀函式、查詢運算式、迭代區塊或非同步方法中 + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12534,16 +12549,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 無法在匿名方法、Lambda 運算式或查詢運算式中使用參考本機 '{0}' - - Iterators cannot have by-reference locals - Iterator 不可有 by-reference local - - - - Async methods cannot have by-reference locals - 非同步方法不可有 by-reference local - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. 對 '{0}' 之呼叫所傳回的參考無法在 'await' 或 'yield' 界限間保留。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 778817985654a..da3d964d5a24a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -133,7 +133,7 @@ private static void VerifyMissingType(string source, WellKnownType type, params // Instrumentation to investigate CI failure: https://github.com/dotnet/roslyn/issues/34207 private CSharpCompilation CreateCompilationWithAsyncIterator(string source, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) => CreateCompilationWithTasksExtensions(new[] { (CSharpTestSource)CSharpTestBase.Parse(source, filename: "source", parseOptions), CSharpTestBase.Parse(AsyncStreamsTypes, filename: "AsyncStreamsTypes", parseOptions) }, - options: options, parseOptions: parseOptions); + options: options); private CSharpCompilation CreateCompilationWithAsyncIterator(CSharpTestSource source, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) => CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: options, parseOptions: parseOptions); @@ -631,15 +631,288 @@ static async System.Threading.Tasks.Task Main() ref struct S { }"; + + var expectedDiagnostics = new[] + { + // source(4,65): error CS0306: The type 'S' may not be used as a type argument + // static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("S").WithLocation(4, 65) + }; + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // source(4,65): error CS0306: The type 'S' may not be used as a type argument // static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("S").WithLocation(4, 65), - // source(11,24): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. + // source(11,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await foreach (var s in M()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(11, 24) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 24)); + } + + [Fact] + public void RefStructElementType_NonGeneric() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + System.Console.Write(s.F); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 238 (0xee) + .maxstack 3 + .locals init (int V_0, + S V_1, //s + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.
d__1 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0016 + IL_0012: br.s IL_0082 + IL_0014: br.s IL_0082 + IL_0016: nop + IL_0017: nop + IL_0018: ldarg.0 + IL_0019: newobj "C..ctor()" + IL_001e: call "E C.GetAsyncEnumerator()" + IL_0023: stfld "E C.
d__1.<>s__1" + IL_0028: br.s IL_0044 + IL_002a: ldarg.0 + IL_002b: ldfld "E C.
d__1.<>s__1" + IL_0030: callvirt "S E.Current.get" + IL_0035: stloc.1 + IL_0036: nop + IL_0037: ldloc.1 + IL_0038: ldfld "int S.F" + IL_003d: call "void System.Console.Write(int)" + IL_0042: nop + IL_0043: nop + IL_0044: ldarg.0 + IL_0045: ldfld "E C.
d__1.<>s__1" + IL_004a: callvirt "System.Threading.Tasks.Task E.MoveNextAsync()" + IL_004f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0054: stloc.2 + IL_0055: ldloca.s V_2 + IL_0057: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_005c: brtrue.s IL_009e + IL_005e: ldarg.0 + IL_005f: ldc.i4.0 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int C.
d__1.<>1__state" + IL_0067: ldarg.0 + IL_0068: ldloc.2 + IL_0069: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_006e: ldarg.0 + IL_006f: stloc.3 + IL_0070: ldarg.0 + IL_0071: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_0076: ldloca.s V_2 + IL_0078: ldloca.s V_3 + IL_007a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__1)" + IL_007f: nop + IL_0080: leave.s IL_00ed + IL_0082: ldarg.0 + IL_0083: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_0088: stloc.2 + IL_0089: ldarg.0 + IL_008a: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_008f: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0095: ldarg.0 + IL_0096: ldc.i4.m1 + IL_0097: dup + IL_0098: stloc.0 + IL_0099: stfld "int C.
d__1.<>1__state" + IL_009e: ldarg.0 + IL_009f: ldloca.s V_2 + IL_00a1: call "bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00a6: stfld "bool C.
d__1.<>s__2" + IL_00ab: ldarg.0 + IL_00ac: ldfld "bool C.
d__1.<>s__2" + IL_00b1: brtrue IL_002a + IL_00b6: ldarg.0 + IL_00b7: ldnull + IL_00b8: stfld "E C.
d__1.<>s__1" + IL_00bd: leave.s IL_00d9 + } + catch System.Exception + { + IL_00bf: stloc.s V_4 + IL_00c1: ldarg.0 + IL_00c2: ldc.i4.s -2 + IL_00c4: stfld "int C.
d__1.<>1__state" + IL_00c9: ldarg.0 + IL_00ca: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_00cf: ldloc.s V_4 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_00d6: nop + IL_00d7: leave.s IL_00ed + } + IL_00d9: ldarg.0 + IL_00da: ldc.i4.s -2 + IL_00dc: stfld "int C.
d__1.<>1__state" + IL_00e1: ldarg.0 + IL_00e2: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_00e7: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00ec: nop + IL_00ed: ret + } + """); + } + + [Fact] + public void RefStructElementType_NonGeneric_AwaitAfter() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + System.Console.Write(s.F); + await Task.Yield(); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void RefStructElementType_NonGeneric_AwaitBefore() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + await Task.Yield(); + System.Console.Write(s.F); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + var expectedDiagnostics = new[] + { + // source(11,34): error CS4007: Instance of type 'S' cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(s.F); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s.F").WithArguments("S").WithLocation(11, 34) + }; + + comp = CreateCompilationWithAsyncIterator(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 0a7f649ff7d64..999bf6667331d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -3789,9 +3789,11 @@ static async Task Main() var comp = CreateCompilationWithMscorlibAndSpan(source, options: options); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( - // (9,66): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' - // await Async1(F1(), G(F2(), stackalloc int[] { 1, 2, 3 }, await F3())); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await F3()").WithArguments("System.Span").WithLocation(9, 66) + // (8,5): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // { + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"{ + await Async1(F1(), G(F2(), stackalloc int[] { 1, 2, 3 }, await F3())); + }").WithArguments("System.Span").WithLocation(8, 5) ); } } @@ -3897,16 +3899,16 @@ public ref struct S public bool P2 => true; } "; - CreateCompilation(source, options: TestOptions.DebugDll).VerifyDiagnostics().VerifyEmitDiagnostics( - // (9,17): error CS4013: Instance of type 'S' cannot be used inside a nested function, query expression, iterator block or async method - // Q { F: { P1: true } } when await c => r, // error: cached Q.F is alive - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "F").WithArguments("S").WithLocation(9, 17) - ); - CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics().VerifyEmitDiagnostics( - // (9,17): error CS4013: Instance of type 'S' cannot be used inside a nested function, query expression, iterator block or async method + + var expectedDiagnostics = new[] + { + // (9,17): error CS4007: Instance of type 'S' cannot be preserved across 'await' or 'yield' boundary. // Q { F: { P1: true } } when await c => r, // error: cached Q.F is alive - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "F").WithArguments("S").WithLocation(9, 17) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "F").WithArguments("S").WithLocation(9, 17) + }; + + CreateCompilation(source, options: TestOptions.DebugDll).VerifyDiagnostics().VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics().VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index bce2519fce4d0..2dcb0b215b19d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -1695,12 +1695,26 @@ public System.Threading.Tasks.Task MoveNextAsync() => throw null; public int Current { get => throw null; } } -}"; - var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable); +}" + s_IAsyncEnumerable; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( - // (6,32): error CS8177: Async methods cannot have by-reference locals + // (6,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await foreach (ref var i in new C()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "i").WithLocation(6, 32)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 32)); + + var expectedDiagnostics = new[] + { + // (6,37): error CS1510: A ref or out value must be an assignable variable + // await foreach (ref var i in new C()) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new C()").WithLocation(6, 37) + }; + + comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source); + comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1708,6 +1722,119 @@ public System.Threading.Tasks.Task MoveNextAsync() Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); } + [Theory, CombinatorialData] + public void TestWithPattern_Ref_Iterator([CombinatorialValues(" ", "readonly")] string modifier) + { + var source = $$""" + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + class C + { + static async Task Main() + { + await foreach (int i in F()) + { + Console.Write(i); + } + } + + static async IAsyncEnumerable F() + { + await foreach (ref {{modifier}} var i in new C()) + { + yield return i; + } + } + + public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => new(); + + public sealed class Enumerator + { + private readonly int[] _array = [1, 2, 3]; + private int _index = -1; + public Task MoveNextAsync() + { + if (_index < _array.Length) _index++; + return Task.FromResult(_index < _array.Length); + } + public ref int Current => ref _array[_index]; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (17,41): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref readonly var i in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(17, 41)); + + var expectedOutput = "123"; + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_Ref_Iterator_Used() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + + class C + { + static async IAsyncEnumerable F() + { + await foreach (ref var i in new C()) + { + yield return i; + M(ref i); + } + } + + static void M(ref int i) { } + + public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => new(); + + public sealed class Enumerator + { + private readonly int[] _array = [1, 2, 3]; + private int _index = -1; + public Task MoveNextAsync() + { + if (_index < _array.Length) _index++; + return Task.FromResult(_index < _array.Length); + } + public ref int Current => ref _array[_index]; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (8,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref var i in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 32)); + + var expectedDiagnostics = new[] + { + // (11,19): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // M(ref i); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "i").WithLocation(11, 19) + }; + + comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + [Fact] public void TestWithPattern_PointerType() { @@ -1935,7 +2062,427 @@ public S(int i) } [Fact] - public void TestWithPattern_RefReturningCurrent() + public void TestWithPattern_RefStructEnumerator_Async() + { + var source = """ + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in new C()) + { + } + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public ref struct Enumerator + { + public int Current => 0; + public Task MoveNextAsync() => throw null; + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,15): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 15) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void TestWithPattern_RefStructEnumerator_AsyncIterator() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + public class C + { + public static async IAsyncEnumerable M() + { + await foreach (var x in new C()) + { + yield return x; + } + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public ref struct Enumerator + { + public int Current => 0; + public Task MoveNextAsync() => throw null; + } + } + """ + s_IAsyncEnumerable; + + var expectedDiagnostics = new[] + { + // (7,15): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct. + // await foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(7, 15) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void TestWithPattern_RefStructEnumerator_Iterator() + { + var source = """ + using System.Collections.Generic; + public class C + { + public static IEnumerable M() + { + foreach (var x in new C()) + { + yield return x; + } + } + public Enumerator GetEnumerator() => new Enumerator(); + public ref struct Enumerator + { + public int Current => 0; + public bool MoveNext() => throw null; + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,9): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct. + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 9) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void TestWithPattern_RefStructEnumerable_Async() + { + var source = """ + using System; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var x in new Enumerable()) + { + await Task.Yield(); + Console.Write($"{x} "); + } + Console.Write("Done"); + } + public ref struct Enumerable + { + public Enumerator GetAsyncEnumerator() => new(); + } + public class Enumerator + { + int i = 0; + public int Current => i; + public async Task MoveNextAsync() + { + i++; + await Task.Yield(); + return i < 3; + } + } + } + """ + s_IAsyncEnumerable; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "1 2 Done").VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefStructEnumerable_AsyncIterator() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var i in M()) + { + Console.Write($"{i} "); + } + Console.Write("Done"); + } + public static async IAsyncEnumerable M() + { + await foreach (var x in new Enumerable()) + { + await Task.Yield(); + yield return x * 2; + } + yield return -1; + } + public ref struct Enumerable + { + public Enumerator GetAsyncEnumerator() => new(); + } + public class Enumerator + { + int i = 0; + public int Current => i; + public async Task MoveNextAsync() + { + i++; + await Task.Yield(); + return i < 3; + } + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2 4 -1 Done").VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefStructEnumerable_Iterator() + { + var source = """ + using System; + using System.Collections.Generic; + public class C + { + public static void Main() + { + foreach (var i in M()) + { + Console.Write($"{i} "); + } + Console.Write("Done"); + } + public static IEnumerable M() + { + foreach (var x in new Enumerable()) + { + yield return x * 2; + } + yield return -1; + } + public ref struct Enumerable + { + public Enumerator GetEnumerator() => new(); + } + public class Enumerator + { + int i = 0; + public int Current => i; + public bool MoveNext() + { + i++; + return i < 3; + } + } + } + """; + CompileAndVerify(source, expectedOutput: "2 4 -1 Done").VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefStructCurrent_Async() + { + var source = """ + using System; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in new C()) + { + Console.Write($"{s.ToString()} "); + } + Console.Write("Done"); + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator : IAsyncDisposable + { + int i = 0; + public S Current => new S(i); + public async Task MoveNextAsync() + { + i++; + await Task.Yield(); + return i < 3; + } + public async ValueTask DisposeAsync() + { + await Task.Yield(); + } + } + } + public ref struct S + { + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() => i.ToString(); + } + """ + s_IAsyncEnumerable; + + var expectedDiagnostics = new[] + { + // (7,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 24) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + + var expectedOutput = "1 2 Done"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefStructCurrent_AsyncIterator() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in M()) + { + Console.Write($"M:{s} "); + } + Console.Write("MainDone"); + } + public static async IAsyncEnumerable M() + { + await foreach (var s in new C()) + { + yield return s.ToString(); + } + yield return "Done"; + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator : IAsyncDisposable + { + int i = 0; + public S Current => new S(i); + public async Task MoveNextAsync() + { + i++; + await Task.Yield(); + return i < 3; + } + public async ValueTask DisposeAsync() + { + await Task.Yield(); + } + } + } + public ref struct S + { + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() => i.ToString(); + } + """ + AsyncStreamsTypes; + + var expectedDiagnostics = new[] + { + // (16,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(16, 24) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + + var expectedOutput = "M:1 M:2 M:Done MainDone"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefStructCurrent_Iterator() + { + var source = """ + using System; + using System.Collections.Generic; + public class C + { + public static void Main() + { + foreach (var s in M()) + { + Console.Write($"M:{s} "); + } + Console.Write("MainDone"); + } + public static IEnumerable M() + { + foreach (var s in new C()) + { + yield return s.ToString(); + } + yield return "Done"; + } + public Enumerator GetEnumerator() => new Enumerator(); + public sealed class Enumerator + { + int i = 0; + public S Current => new S(i); + public bool MoveNext() + { + i++; + return i < 3; + } + } + } + public ref struct S + { + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() => i.ToString(); + } + """; + + var expectedOutput = "M:1 M:2 M:Done MainDone"; + + CompileAndVerify(source, parseOptions: TestOptions.Regular12, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefReturningCurrent_Async() { string source = @" using static System.Console; @@ -1986,6 +2533,275 @@ public override string ToString() CompileAndVerify(comp, expectedOutput: "1 2 3 Done", verify: Verification.Fails); } + [Fact] + public void TestWithPattern_RefReturningCurrent_Async_RefVariable() + { + string source = """ + using System; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (ref var s in new C()) + { + Console.Write($"{s} "); + s.F++; + } + Console.Write("Done"); + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator + { + S _current; + public ref S Current => ref _current; + public async Task MoveNextAsync() + { + Current = new S(Current.F + 1); + await Task.Yield(); + return Current.F < 4; + } + } + } + public struct S + { + public int F; + public S(int i) + { + this.F = i; + } + public override string ToString() => F.ToString(); + } + """ + s_IAsyncEnumerable; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 32)); + + var expectedOutput = "1 3 Done"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefReturningCurrent_AsyncIterator_RefVariable_01() + { + string source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in M()) + { + Console.Write($"M:{s} "); + } + Console.Write("MainDone"); + } + public static async IAsyncEnumerable M() + { + await foreach (ref var s in new C()) + { + s.F++; + yield return s.ToString(); + } + yield return "Done"; + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator + { + S _current; + public ref S Current => ref _current; + public async Task MoveNextAsync() + { + Current = new S(Current.F + 1); + await Task.Yield(); + return Current.F < 4; + } + } + } + public struct S + { + public int F; + public S(int i) + { + this.F = i; + } + public override string ToString() => F.ToString(); + } + """ + AsyncStreamsTypes; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (16,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s").WithArguments("ref and unsafe in async and iterator methods").WithLocation(16, 32)); + + var expectedOutput = "M:2 M:4 M:Done MainDone"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefReturningCurrent_AsyncIterator_RefVariable_02() + { + string source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + public class C + { + public static async IAsyncEnumerable M() + { + await foreach (ref var s in new C()) + { + yield return s.ToString(); + s.F++; + } + yield return "Done"; + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator + { + public ref S Current => throw null; + public Task MoveNextAsync() => throw null; + } + } + public struct S + { + public int F; + } + """ + AsyncStreamsTypes; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 32)); + + var expectedDiagnostics = new[] + { + // (10,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // s.F++; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "s.F").WithLocation(10, 13) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void TestWithPattern_RefReturningCurrent_Iterator_RefVariable_01() + { + string source = """ + using System; + using System.Collections.Generic; + public class C + { + public static void Main() + { + foreach (var s in M()) + { + Console.Write($"M:{s} "); + } + Console.Write("MainDone"); + } + public static IEnumerable M() + { + foreach (ref var s in new C()) + { + s.F++; + yield return s.ToString(); + } + yield return "Done"; + } + public Enumerator GetEnumerator() => new Enumerator(); + public sealed class Enumerator + { + S _current; + public ref S Current => ref _current; + public bool MoveNext() + { + Current = new S(Current.F + 1); + return Current.F < 4; + } + } + } + public struct S + { + public int F; + public S(int i) + { + this.F = i; + } + public override string ToString() => F.ToString(); + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (15,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s").WithArguments("ref and unsafe in async and iterator methods").WithLocation(15, 26)); + + var expectedOutput = "M:2 M:4 M:Done MainDone"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_RefReturningCurrent_Iterator_RefVariable_02() + { + string source = """ + using System.Collections.Generic; + public class C + { + public static IEnumerable M() + { + foreach (ref var s in new C()) + { + yield return s.ToString(); + s.F++; + } + yield return "Done"; + } + public Enumerator GetEnumerator() => new Enumerator(); + public sealed class Enumerator + { + public ref S Current => throw null; + public bool MoveNext() => throw null; + } + } + public struct S + { + public int F; + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 26)); + + var expectedDiagnostics = new[] + { + // (9,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // s.F++; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "s.F").WithLocation(9, 13) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + [Fact] public void TestWithPattern_IterationVariableIsReadOnly() { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index c388178fb6771..7282e99ed475f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -3084,5 +3084,440 @@ public void Dispose() comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "DISPOSED"); } + + [Fact] + public void RefStruct_AwaitInside() + { + var source = """ + using System.Threading.Tasks; + class C + { + async Task M() + { + using (new R()) + { + await Task.Yield(); + } + } + } + ref struct R + { + public void Dispose() { } + } + """; + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,16): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 16)); + + var expectedDiagnostics = new[] + { + // (6,16): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // using (new R()) + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new R()").WithArguments("R").WithLocation(6, 16) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_YieldReturnInside() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M() + { + using (new R()) + { + yield return 1; + } + } + } + ref struct R + { + public void Dispose() { } + } + """; + + var expectedDiagnostics = new[] + { + // (6,16): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // using (new R()) + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new R()").WithArguments("R").WithLocation(6, 16) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_YieldBreakInside() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var x in M(true)) { Console.Write(x); } + Console.Write(" "); + foreach (var x in M(false)) { Console.Write(x); } + } + static IEnumerable M(bool b) + { + yield return 123; + using (new R()) + { + if (b) { yield break; } + } + yield return 456; + } + } + ref struct R + { + public R() => Console.Write("C"); + public void Dispose() => Console.Write("D"); + } + """; + + var expectedOutput = "123CD 123CD456"; + + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.Regular12).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_AwaitResource() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + Console.Write("1"); + using ((await GetC()).GetR()) + { + Console.Write("2"); + } + Console.Write("3"); + } + static async Task GetC() + { + Console.Write("Ga"); + await Task.Yield(); + Console.Write("Gb"); + return new C(); + } + R GetR() => new R(); + } + ref struct R + { + public R() => Console.Write("C"); + public void Dispose() => Console.Write("D"); + } + """; + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,16): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // using ((await GetC()).GetR()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "(await GetC()).GetR()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 16)); + + var expectedOutput = "1GaGbC2D3"; + + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext, verify: Verification.FailsILVerify).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_AwaitOutside() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + Console.Write("1"); + await Task.Yield(); + Console.Write("2"); + using (new R()) + { + Console.Write("3"); + } + Console.Write("4"); + await Task.Yield(); + Console.Write("5"); + } + } + ref struct R + { + public R() => Console.Write("C"); + public void Dispose() => Console.Write("D"); + } + """; + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (10,16): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 16)); + + var expectedOutput = "12C3D45"; + + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_YieldReturnOutside() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var x in M()) + { + Console.Write(x); + } + } + static IEnumerable M() + { + Console.Write("1"); + yield return "a"; + Console.Write("2"); + using (new R()) + { + Console.Write("3"); + } + Console.Write("4"); + yield return "b"; + Console.Write("5"); + } + } + ref struct R + { + public R() => Console.Write("C"); + public void Dispose() => Console.Write("D"); + } + """; + + var expectedOutput = "1a2C3D4b5"; + + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.Regular12).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_AwaitUsing() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + Console.Write("1"); + await using (new R()) + { + Console.Write("2"); + } + Console.Write("3"); + } + } + ref struct R + { + public R() => Console.Write("C"); + public ValueTask DisposeAsync() + { + Console.Write("D"); + return default; + } + } + """; + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 22)); + + var expectedOutput = "1C2D3"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_AwaitUsing_AwaitInside() + { + var source = """ + using System.Threading.Tasks; + class C + { + async Task M() + { + await using (new R()) + { + await Task.Yield(); + } + } + } + ref struct R + { + public ValueTask DisposeAsync() => default; + } + """; + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 22)); + + var expectedDiagnostics = new[] + { + // (6,22): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // await using (new R()) + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new R()").WithArguments("R").WithLocation(6, 22) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_AwaitUsing_YieldReturnInside() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + async IAsyncEnumerable M() + { + await using (new R()) + { + yield return 123; + } + } + } + ref struct R + { + public ValueTask DisposeAsync() => default; + } + """ + AsyncStreamsTypes; + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22)); + + var expectedDiagnostics = new[] + { + // (7,22): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // await using (new R()) + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new R()").WithArguments("R").WithLocation(7, 22) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_AwaitUsing_YieldReturnInside_Var() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + async IAsyncEnumerable M() + { + await using var _ = new R(); + yield return 123; + } + } + ref struct R + { + public ValueTask DisposeAsync() => default; + } + """ + AsyncStreamsTypes; + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,21): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await using var _ = new R(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 21)); + + var expectedDiagnostics = new[] + { + // (7,25): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // await using var _ = new R(); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "_ = new R()").WithArguments("R").WithLocation(7, 25) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_AwaitUsing_YieldBreakInside() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var x in M(true)) { Console.Write(x); } + Console.Write(" "); + await foreach (var x in M(false)) { Console.Write(x); } + } + static async IAsyncEnumerable M(bool b) + { + yield return 1; + await using (new R()) + { + if (b) { yield break; } + } + yield return 2; + } + } + ref struct R + { + public R() => Console.Write("C"); + public ValueTask DisposeAsync() + { + Console.Write("D"); + return default; + } + } + """ + AsyncStreamsTypes; + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (15,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await using (new R()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new R()").WithArguments("ref and unsafe in async and iterator methods").WithLocation(15, 22)); + + var expectedOutput = "1CD 1CD2"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs index 8aa5ab4c25073..bd68eefaa1e3e 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs @@ -606,6 +606,59 @@ public void GotoInLambda_NonExistent() Diagnostic(ErrorCode.ERR_LabelNotFound, "x").WithArguments("x").WithLocation(4, 10)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_OutOfScope_Backward() + { + var code = """ + #pragma warning disable CS8321 // local function unused + x: + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (6,5): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "goto").WithArguments("x").WithLocation(6, 5)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_OutOfScope_Forward() + { + var code = """ + #pragma warning disable CS8321 // local function unused + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + x:; + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (5,5): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "goto").WithArguments("x").WithLocation(5, 5)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_NonExistent() + { + var code = """ + #pragma warning disable CS8321 // local function unused + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (5,10): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "x").WithArguments("x").WithLocation(5, 10)); + } + // Definition same label in different lambdas [WorkItem(5991, "DevDiv_Projects/Roslyn")] [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs index 48efeed6dbc2b..f0d02647d79a9 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs @@ -47,7 +47,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0014: ret } @@ -60,7 +60,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" @@ -104,7 +104,7 @@ .locals init (char V_0) IL_0007: call "char char.ToLowerInvariant(char)" IL_000c: stloc.0 IL_000d: ldloca.s V_0 - IL_000f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0014: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0019: ret } @@ -118,7 +118,7 @@ .locals init (char V_0) IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" @@ -179,7 +179,7 @@ .locals init (char V_0) IL_000a: call "char Test.GetCharWithSideEffect()" IL_000f: stloc.0 IL_0010: ldloca.s V_0 - IL_0012: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0012: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0017: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_001c: ret } @@ -192,7 +192,7 @@ .locals init (char V_0) IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" @@ -240,13 +240,13 @@ .locals init (char V_0, IL_0001: ldfld "char C.c" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "ref char C.GetC()" IL_0014: ldind.u2 IL_0015: stloc.1 IL_0016: ldloca.s V_1 - IL_0018: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0018: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001d: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0022: call "void System.Console.Write(string)" IL_0027: ret @@ -293,7 +293,7 @@ .locals init (char V_0, //c IL_0003: ldloc.0 IL_0004: stloc.1 IL_0005: ldloca.s V_1 - IL_0007: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0007: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000c: ldarg.0 IL_000d: ldloca.s V_0 IL_000f: call "string C.SneakyLocalChange(ref char)" @@ -497,11 +497,11 @@ .locals init (char V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.1 IL_000a: stloc.1 IL_000b: ldloca.s V_1 - IL_000d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0012: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0017: ret } @@ -943,7 +943,7 @@ .locals init (int V_0, IL_00cd: ldfld "string Test.d__1.<>7__wrap1" IL_00d2: call "System.ReadOnlySpan string.op_Implicit(string)" IL_00d7: ldloca.s V_2 - IL_00d9: newobj "System.ReadOnlySpan..ctor(in char)" + IL_00d9: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_00de: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_00e3: stloc.1 IL_00e4: leave.s IL_00ff @@ -1014,7 +1014,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0014: ret } @@ -1027,7 +1027,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" @@ -1081,7 +1081,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -1100,7 +1100,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -1119,7 +1119,7 @@ .locals init (char V_0) IL_000c: ldarg.1 IL_000d: stloc.0 IL_000e: ldloca.s V_0 - IL_0010: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_001a: ret } @@ -1133,13 +1133,13 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.1 IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_001d: ret } @@ -1192,7 +1192,7 @@ .locals init (char V_0) IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: ldarg.0 @@ -1212,7 +1212,7 @@ .locals init (char V_0) IL_0007: call "char char.ToLowerInvariant(char)" IL_000c: stloc.0 IL_000d: ldloca.s V_0 - IL_000f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0014: ldarg.0 IL_0015: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001a: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -1232,7 +1232,7 @@ .locals init (char V_0) IL_000d: call "char char.ToLowerInvariant(char)" IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0015: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001a: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_001f: ret } @@ -1247,14 +1247,14 @@ .locals init (char V_0, IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: ldarg.1 IL_0015: call "char char.ToLowerInvariant(char)" IL_001a: stloc.1 IL_001b: ldloca.s V_1 - IL_001d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0022: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0027: ret } @@ -1297,7 +1297,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -1371,7 +1371,7 @@ .locals init (char V_0) IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "string Test.GetStringWithSideEffect()" @@ -1390,7 +1390,7 @@ .locals init (char V_0) IL_000a: call "char Test.GetCharWithSideEffect()" IL_000f: stloc.0 IL_0010: ldloca.s V_0 - IL_0012: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0012: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0017: call "string Test.GetStringWithSideEffect()" IL_001c: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0021: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -1409,7 +1409,7 @@ .locals init (char V_0) IL_0014: call "char Test.GetCharWithSideEffect()" IL_0019: stloc.0 IL_001a: ldloca.s V_0 - IL_001c: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001c: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0021: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0026: ret } @@ -1423,13 +1423,13 @@ .locals init (char V_0, IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "char Test.GetCharWithSideEffect()" IL_001c: stloc.1 IL_001d: ldloca.s V_1 - IL_001f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0024: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0029: ret } @@ -1484,18 +1484,18 @@ .locals init (char V_0, IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: ldloca.s V_0 - IL_0005: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0005: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000a: ldarg.0 IL_000b: ldfld "char C.c" IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "ref char C.GetC()" IL_001e: ldind.u2 IL_001f: stloc.2 IL_0020: ldloca.s V_2 - IL_0022: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0022: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0027: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002c: call "void System.Console.Write(string)" IL_0031: ret @@ -1553,17 +1553,17 @@ .locals init (char V_0, //c IL_0003: ldc.i4.s 97 IL_0005: stloc.1 IL_0006: ldloca.s V_1 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: ldloc.0 IL_000e: stloc.2 IL_000f: ldloca.s V_2 - IL_0011: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0011: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0016: ldarg.0 IL_0017: ldloca.s V_0 IL_0019: call "char C.SneakyLocalChange(ref char)" IL_001e: stloc.3 IL_001f: ldloca.s V_3 - IL_0021: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0021: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0026: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002b: ret } @@ -1616,7 +1616,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0014: ret } @@ -1802,15 +1802,15 @@ .locals init (char V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.1 IL_000a: stloc.1 IL_000b: ldloca.s V_1 - IL_000d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0012: ldarg.2 IL_0013: stloc.2 IL_0014: ldloca.s V_2 - IL_0016: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0020: ret } @@ -2004,7 +2004,7 @@ .locals init (char V_0) IL_0011: ldarg.2 IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0015: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001a: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_001f: ret } @@ -2017,7 +2017,7 @@ .locals init (char V_0) IL_0000: ldarg.2 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -2530,7 +2530,7 @@ .locals init (int V_0, IL_0142: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0147: ldarg.0 IL_0148: ldflda "char Test.d__1.<>7__wrap1" - IL_014d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_014d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0152: ldloc.3 IL_0153: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0158: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -2605,7 +2605,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -2624,7 +2624,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -2643,7 +2643,7 @@ .locals init (char V_0) IL_000c: ldarg.1 IL_000d: stloc.0 IL_000e: ldloca.s V_0 - IL_0010: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_001a: ret } @@ -2657,13 +2657,13 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.1 IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_001d: ret } @@ -2722,7 +2722,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -2743,7 +2743,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: ldarg.0 @@ -2764,7 +2764,7 @@ .locals init (char V_0) IL_000c: ldarg.1 IL_000d: stloc.0 IL_000e: ldloca.s V_0 - IL_0010: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0015: ldarg.0 IL_0016: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -2785,7 +2785,7 @@ .locals init (char V_0) IL_0012: ldarg.1 IL_0013: stloc.0 IL_0014: ldloca.s V_0 - IL_0016: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0020: ret } @@ -2799,13 +2799,13 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.1 IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -2823,13 +2823,13 @@ .locals init (char V_0, IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: ldarg.1 IL_0016: stloc.1 IL_0017: ldloca.s V_1 - IL_0019: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0023: ret } @@ -2843,7 +2843,7 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -2851,7 +2851,7 @@ .locals init (char V_0, IL_0015: ldarg.1 IL_0016: stloc.1 IL_0017: ldloca.s V_1 - IL_0019: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0023: ret } @@ -2911,7 +2911,7 @@ .locals init (char V_0) IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: ldarg.0 @@ -2933,7 +2933,7 @@ .locals init (char V_0) IL_0007: call "char char.ToLowerInvariant(char)" IL_000c: stloc.0 IL_000d: ldloca.s V_0 - IL_000f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0014: ldarg.0 IL_0015: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001a: ldarg.0 @@ -2955,7 +2955,7 @@ .locals init (char V_0) IL_000d: call "char char.ToLowerInvariant(char)" IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0015: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001a: ldarg.0 IL_001b: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0020: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -2977,7 +2977,7 @@ .locals init (char V_0) IL_0013: call "char char.ToLowerInvariant(char)" IL_0018: stloc.0 IL_0019: ldloca.s V_0 - IL_001b: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001b: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0020: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0025: ret } @@ -2992,14 +2992,14 @@ .locals init (char V_0, IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: ldarg.1 IL_0015: call "char char.ToLowerInvariant(char)" IL_001a: stloc.1 IL_001b: ldloca.s V_1 - IL_001d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0022: ldarg.0 IL_0023: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0028: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -3018,14 +3018,14 @@ .locals init (char V_0, IL_0007: call "char char.ToLowerInvariant(char)" IL_000c: stloc.0 IL_000d: ldloca.s V_0 - IL_000f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0014: ldarg.0 IL_0015: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001a: ldarg.1 IL_001b: call "char char.ToLowerInvariant(char)" IL_0020: stloc.1 IL_0021: ldloca.s V_1 - IL_0023: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0023: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0028: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002d: ret } @@ -3040,7 +3040,7 @@ .locals init (char V_0, IL_0001: call "char char.ToLowerInvariant(char)" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000e: ldarg.0 IL_000f: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0014: ldarg.0 @@ -3049,7 +3049,7 @@ .locals init (char V_0, IL_001b: call "char char.ToLowerInvariant(char)" IL_0020: stloc.1 IL_0021: ldloca.s V_1 - IL_0023: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0023: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0028: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002d: ret } @@ -3100,7 +3100,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: ldarg.0 @@ -3186,7 +3186,7 @@ .locals init (char V_0) IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "string Test.GetStringWithSideEffect()" @@ -3207,7 +3207,7 @@ .locals init (char V_0) IL_000a: call "char Test.GetCharWithSideEffect()" IL_000f: stloc.0 IL_0010: ldloca.s V_0 - IL_0012: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0012: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0017: call "string Test.GetStringWithSideEffect()" IL_001c: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0021: call "string Test.GetStringWithSideEffect()" @@ -3228,7 +3228,7 @@ .locals init (char V_0) IL_0014: call "char Test.GetCharWithSideEffect()" IL_0019: stloc.0 IL_001a: ldloca.s V_0 - IL_001c: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001c: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0021: call "string Test.GetStringWithSideEffect()" IL_0026: call "System.ReadOnlySpan string.op_Implicit(string)" IL_002b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -3249,7 +3249,7 @@ .locals init (char V_0) IL_001e: call "char Test.GetCharWithSideEffect()" IL_0023: stloc.0 IL_0024: ldloca.s V_0 - IL_0026: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0026: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_002b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0030: ret } @@ -3263,13 +3263,13 @@ .locals init (char V_0, IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "char Test.GetCharWithSideEffect()" IL_001c: stloc.1 IL_001d: ldloca.s V_1 - IL_001f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0024: call "string Test.GetStringWithSideEffect()" IL_0029: call "System.ReadOnlySpan string.op_Implicit(string)" IL_002e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -3287,13 +3287,13 @@ .locals init (char V_0, IL_000a: call "char Test.GetCharWithSideEffect()" IL_000f: stloc.0 IL_0010: ldloca.s V_0 - IL_0012: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0012: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0017: call "string Test.GetStringWithSideEffect()" IL_001c: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0021: call "char Test.GetCharWithSideEffect()" IL_0026: stloc.1 IL_0027: ldloca.s V_1 - IL_0029: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0029: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_002e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0033: ret } @@ -3307,7 +3307,7 @@ .locals init (char V_0, IL_0000: call "char Test.GetCharWithSideEffect()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 - IL_0008: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000d: call "string Test.GetStringWithSideEffect()" IL_0012: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0017: call "string Test.GetStringWithSideEffect()" @@ -3315,7 +3315,7 @@ .locals init (char V_0, IL_0021: call "char Test.GetCharWithSideEffect()" IL_0026: stloc.1 IL_0027: ldloca.s V_1 - IL_0029: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0029: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_002e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0033: ret } @@ -3374,24 +3374,24 @@ .locals init (char V_0, IL_0000: ldc.i4.s 97 IL_0002: stloc.0 IL_0003: ldloca.s V_0 - IL_0005: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0005: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000a: ldarg.0 IL_000b: ldfld "char C.c" IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "ref char C.GetC()" IL_001e: ldind.u2 IL_001f: stloc.2 IL_0020: ldloca.s V_2 - IL_0022: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0022: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0027: ldarg.0 IL_0028: call "ref char C.GetC2()" IL_002d: ldind.u2 IL_002e: stloc.3 IL_002f: ldloca.s V_3 - IL_0031: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0031: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0036: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_003b: call "void System.Console.Write(string)" IL_0040: ret @@ -3453,7 +3453,7 @@ .locals init (char V_0, //c1 IL_0006: ldloc.0 IL_0007: stloc.2 IL_0008: ldloca.s V_2 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: ldloca.s V_0 IL_0012: call "string C.SneakyLocalChange(ref char)" @@ -3461,7 +3461,7 @@ .locals init (char V_0, //c1 IL_001c: ldloc.1 IL_001d: stloc.3 IL_001e: ldloca.s V_3 - IL_0020: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0020: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0025: ldarg.0 IL_0026: ldloca.s V_1 IL_0028: call "string C.SneakyLocalChange(ref char)" @@ -3525,7 +3525,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -3767,19 +3767,19 @@ .locals init (char V_0, IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.1 IL_000a: stloc.1 IL_000b: ldloca.s V_1 - IL_000d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0012: ldarg.2 IL_0013: stloc.2 IL_0014: ldloca.s V_2 - IL_0016: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001b: ldarg.3 IL_001c: stloc.3 IL_001d: ldloca.s V_3 - IL_001f: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001f: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0024: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0029: ret } @@ -4045,11 +4045,11 @@ .locals init (char V_0, IL_0011: ldarg.2 IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0015: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001a: ldarg.2 IL_001b: stloc.1 IL_001c: ldloca.s V_1 - IL_001e: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001e: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0023: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0028: ret } @@ -4063,11 +4063,11 @@ .locals init (char V_0, IL_0000: ldarg.2 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.2 IL_000a: stloc.1 IL_000b: ldloca.s V_1 - IL_000d: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000d: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0012: ldarg.0 IL_0013: ldarg.1 IL_0014: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -4086,7 +4086,7 @@ .locals init (char V_0, IL_0000: ldarg.2 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -4095,7 +4095,7 @@ .locals init (char V_0, IL_001a: ldarg.2 IL_001b: stloc.1 IL_001c: ldloca.s V_1 - IL_001e: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001e: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0023: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0028: ret } @@ -4151,7 +4151,7 @@ .locals init (char V_0) IL_0017: ldarg.3 IL_0018: stloc.0 IL_0019: ldloca.s V_0 - IL_001b: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001b: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0020: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0025: ret } @@ -4169,7 +4169,7 @@ .locals init (char V_0) IL_0011: ldarg.3 IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0015: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001a: ldarg.2 IL_001b: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0020: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -4186,7 +4186,7 @@ .locals init (char V_0) IL_0006: ldarg.3 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: ldarg.1 IL_0011: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -4204,7 +4204,7 @@ .locals init (char V_0) IL_0000: ldarg.3 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.2 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -4231,7 +4231,7 @@ .locals init (char V_0) IL_0017: ldarg.3 IL_0018: stloc.0 IL_0019: ldloca.s V_0 - IL_001b: newobj "System.ReadOnlySpan..ctor(in char)" + IL_001b: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0020: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0025: ret } @@ -4244,7 +4244,7 @@ .locals init (char V_0) IL_0000: ldarg.3 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -4415,7 +4415,7 @@ .locals init (char V_0) IL_0012: ldarg.3 IL_0013: stloc.0 IL_0014: ldloca.s V_0 - IL_0016: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" IL_0020: ret } @@ -4428,7 +4428,7 @@ .locals init (char V_0) IL_0000: ldarg.3 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" @@ -5229,10 +5229,10 @@ .locals init (int V_0, IL_01af: call "System.ReadOnlySpan string.op_Implicit(string)" IL_01b4: ldarg.0 IL_01b5: ldflda "char Test.d__1.<>7__wrap1" - IL_01ba: newobj "System.ReadOnlySpan..ctor(in char)" + IL_01ba: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_01bf: ldarg.0 IL_01c0: ldflda "char Test.d__1.<>7__wrap2" - IL_01c5: newobj "System.ReadOnlySpan..ctor(in char)" + IL_01c5: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_01ca: ldloc.s V_4 IL_01cc: call "System.ReadOnlySpan string.op_Implicit(string)" IL_01d1: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -5313,7 +5313,7 @@ .locals init (char V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -5334,7 +5334,7 @@ .locals init (char V_0) IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: ldarg.0 @@ -5355,7 +5355,7 @@ .locals init (char V_0) IL_000c: ldarg.1 IL_000d: stloc.0 IL_000e: ldloca.s V_0 - IL_0010: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0015: ldarg.0 IL_0016: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -5376,7 +5376,7 @@ .locals init (char V_0) IL_0012: ldarg.1 IL_0013: stloc.0 IL_0014: ldloca.s V_0 - IL_0016: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0020: ret } @@ -5390,13 +5390,13 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.1 IL_0010: stloc.1 IL_0011: ldloca.s V_1 - IL_0013: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "System.ReadOnlySpan string.op_Implicit(string)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" @@ -5414,13 +5414,13 @@ .locals init (char V_0, IL_0006: ldarg.1 IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: newobj "System.ReadOnlySpan..ctor(in char)" + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_000f: ldarg.0 IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" IL_0015: ldarg.1 IL_0016: stloc.1 IL_0017: ldloca.s V_1 - IL_0019: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0023: ret } @@ -5434,7 +5434,7 @@ .locals init (char V_0, IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 - IL_0004: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0009: ldarg.0 IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000f: ldarg.0 @@ -5442,7 +5442,7 @@ .locals init (char V_0, IL_0015: ldarg.1 IL_0016: stloc.1 IL_0017: ldloca.s V_1 - IL_0019: newobj "System.ReadOnlySpan..ctor(in char)" + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_0023: ret } diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 028ff1e8ff7d4..d2f3bf455c812 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5463,7 +5463,10 @@ static IEnumerable F() var compilation1 = compilation0.WithSource(source1.Tree); var v0 = CompileAndVerify(compilation0); - v0.VerifyDiagnostics(); + v0.VerifyDiagnostics( + // (17,34): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return 1; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(17, 34)); var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); var f0 = compilation0.GetMember("C.F"); diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs index 04b2a6d0025ee..41ff3e1fd848c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs @@ -1036,14 +1036,29 @@ static void G() {} Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default),// add new row ]); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), + ]); + } + else + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), + ]); + } }) .Verify(); } @@ -1422,15 +1437,32 @@ [A7]void H() { } Handle(9, TableIndex.CustomAttribute), ]); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete + new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] + ]); + } + else + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 + ]); + + } }) .AddGeneration( source: common + """ @@ -1472,15 +1504,32 @@ void G() { } Handle(11, TableIndex.CustomAttribute), ]); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 + ]); + } + else + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 + ]); + + } }) .AddGeneration( source: common + """ @@ -1648,11 +1697,23 @@ [A4] void G() { } Handle(5, TableIndex.CustomAttribute), ]); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] - new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] + new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] + ]); + } + else + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] + new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] + ]); + } }) .Verify(); } @@ -3024,15 +3085,23 @@ class C g.VerifyTypeDefNames("E", "C", "D"); g.VerifyMethodDefNames(); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(1, TableIndex.MethodDef)), // E - new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(3, TableIndex.MethodDef)), // C - new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(6, TableIndex.MethodDef)), // D - new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(2, TableIndex.MethodDef)), // E.A - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(4, TableIndex.MethodDef)), // _x - new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(5, TableIndex.MethodDef)) // X - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(5, TableIndex.MethodDef)), // X + new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(2, TableIndex.MethodDef)), // E.A + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(4, TableIndex.MethodDef)), // _x + new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(1, TableIndex.MethodDef)), // E + new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(3, TableIndex.MethodDef)), // C + new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(6, TableIndex.MethodDef)), // D + ]); + } + else + { + + } g.VerifyEncLogDefinitions( [ @@ -3105,15 +3174,31 @@ class C g.VerifyTypeDefNames("E", "C", "D"); g.VerifyMethodDefNames(); - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E - new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C - new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D - new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x - new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)) // X - ]); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)),// X + new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x + new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E + new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C + new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D + ]); + } + else + { + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E + new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C + new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D + new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x + new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)),// X + ]); + } g.VerifyEncLogDefinitions(new[] { @@ -6964,16 +7049,33 @@ [B] static void M2<[A]T>() { } Handle(6, TableIndex.MethodSemantics), Handle(2, TableIndex.GenericParam)); - CheckAttributes(reader1, - new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef))); + // https://github.com/dotnet/roslyn/issues/73513 + if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) + { + CheckAttributes(reader1, + new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef))); + } + else + { + CheckAttributes(reader1, + new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef))); + } } /// diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs index 4315771543ce5..000ddcb8d0bce 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs @@ -5520,7 +5520,7 @@ static void Main() Main: Entered Main: L'a' = 1 Main: L'b' = 2 -Main: L4 = System.Linq.Enumerable+SelectArrayIterator`2[System.Int32,System.Int32] +Main: L4 = System.Linq.Enumerable+ArraySelectIterator`2[System.Int32,System.Int32] Main: Entered lambda '
b__0'
b__0: P'item'[0] = 10
b__0: Returned diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs index 4760ca1c2c254..458de9f14813a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs @@ -1359,20 +1359,44 @@ static IEnumerable F() yield return sizeof(System.UIntPtr); } }"; - 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 + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + var expectedDiagnostics = new[] + { + // (6,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(nint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nint)").WithLocation(6, 22), - // (7,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 22), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(nuint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nuint)").WithLocation(7, 22), - // (8,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nuint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22), + // (8,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(System.IntPtr); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(System.IntPtr)").WithLocation(8, 22), - // (9,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(System.IntPtr)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 22), + // (9,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(System.UIntPtr); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(System.UIntPtr)").WithLocation(9, 22)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(System.UIntPtr)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22), + // (7,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nuint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nuint)").WithArguments("nuint").WithLocation(7, 22), + // (8,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(System.IntPtr); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(System.IntPtr)").WithArguments("nint").WithLocation(8, 22), + // (9,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(System.UIntPtr); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(System.UIntPtr)").WithArguments("nuint").WithLocation(9, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); } [Fact] 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 3276fae69d6c0..aa297ca0ff6be 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 - $(NetRoslyn);net472 + $(NetRoslynNext);net472 true diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 47f7b9a565775..093d96cf9995c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -13967,7 +13967,7 @@ static void M2() comp, symbolValidator: module => { - AssertEx.Equal(new[] { "<>y__InlineArray1", "<>y__InlineArray3" }, getInlineArrayTypeNames(module)); + AssertEx.Equal(new[] { "<>y__InlineArray3" }, getInlineArrayTypeNames(module)); }, verify: Verification.Skipped); @@ -18053,6 +18053,8 @@ public class MyCollectionBuilder var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); + // https://github.com/dotnet/roslyn/issues/73085 + // Test hits an assertion failure when collection-expr with a single element is used here string sourceB = """ #pragma warning disable 219 class Program @@ -18060,7 +18062,7 @@ class Program static void Main() { MyCollection x = []; - MyCollection y = ["2"]; + MyCollection y = ["2", "3"]; MyCollection z = new(); } } @@ -18069,14 +18071,17 @@ static void Main() comp.MakeTypeMissing(SpecialType.System_Int32); comp.VerifyEmitDiagnostics( // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported - // MyCollection y = ["2"]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2""]").WithArguments("System.Int32").WithLocation(7, 34), + // MyCollection y = ["2", "3"]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported - // MyCollection y = ["2"]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2""]").WithArguments("System.Int32").WithLocation(7, 34), + // MyCollection y = ["2", "3"]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported - // MyCollection y = ["2"]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2""]").WithArguments("System.Int32").WithLocation(7, 34)); + // MyCollection y = ["2", "3"]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), + // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported + // MyCollection y = ["2", "3"]; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34)); } [Fact] @@ -20911,6 +20916,397 @@ static void Report(MyCollection c) Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y, z]").WithArguments("MyCollection").WithLocation(12, 60)); } + [Fact] + public void Span_SingleElement() + { + var source = """ + using System; + + class Program + { + static void Main() => M(1); + + static void M(int x) + { + Span y = [x]; + x++; + Console.Write(y[0]); + Console.Write(x); + } + } + """; + + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 36 (0x24) + .maxstack 2 + .locals init (System.Span V_0, //y + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloca.s V_1 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: stloc.0 + IL_000a: ldarg.0 + IL_000b: ldc.i4.1 + IL_000c: add + IL_000d: starg.s V_0 + IL_000f: ldloca.s V_0 + IL_0011: ldc.i4.0 + IL_0012: call "ref int System.Span.this[int].get" + IL_0017: ldind.i4 + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldarg.0 + IL_001e: call "void System.Console.Write(int)" + IL_0023: ret + } + """); + + verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 43 (0x2b) + .maxstack 5 + .locals init (System.Span V_0) //y + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldarg.0 + IL_000b: stelem.i4 + IL_000c: call "System.Span..ctor(int[])" + IL_0011: ldarg.0 + IL_0012: ldc.i4.1 + IL_0013: add + IL_0014: starg.s V_0 + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.0 + IL_0019: call "ref int System.Span.this[int].get" + IL_001e: ldind.i4 + IL_001f: call "void System.Console.Write(int)" + IL_0024: ldarg.0 + IL_0025: call "void System.Console.Write(int)" + IL_002a: ret + } + """); + } + + [Fact] + public void Span_SingleElement_TempsAreNotReused() + { + var source = """ + using System; + + class Program + { + static void Main() => M(1); + + static void M(int x) + { + { + Span y = [x]; + Console.Write(y[0]); + y[0]++; + } + { + Span y = [x]; + Console.Write(y[0]); + } + } + } + """; + + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 62 (0x3e) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Span V_2, //y + System.Span V_3) //y + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: stloc.2 + IL_000a: ldloca.s V_2 + IL_000c: ldc.i4.0 + IL_000d: call "ref int System.Span.this[int].get" + IL_0012: ldind.i4 + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloca.s V_2 + IL_001a: ldc.i4.0 + IL_001b: call "ref int System.Span.this[int].get" + IL_0020: dup + IL_0021: ldind.i4 + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: stind.i4 + IL_0025: ldarg.0 + IL_0026: stloc.1 + IL_0027: ldloca.s V_1 + IL_0029: newobj "System.Span..ctor(ref int)" + IL_002e: stloc.3 + IL_002f: ldloca.s V_3 + IL_0031: ldc.i4.0 + IL_0032: call "ref int System.Span.this[int].get" + IL_0037: ldind.i4 + IL_0038: call "void System.Console.Write(int)" + IL_003d: ret + } + """); + + verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 76 (0x4c) + .maxstack 5 + .locals init (System.Span V_0, //y + System.Span V_1) //y + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldarg.0 + IL_000b: stelem.i4 + IL_000c: call "System.Span..ctor(int[])" + IL_0011: ldloca.s V_0 + IL_0013: ldc.i4.0 + IL_0014: call "ref int System.Span.this[int].get" + IL_0019: ldind.i4 + IL_001a: call "void System.Console.Write(int)" + IL_001f: ldloca.s V_0 + IL_0021: ldc.i4.0 + IL_0022: call "ref int System.Span.this[int].get" + IL_0027: dup + IL_0028: ldind.i4 + IL_0029: ldc.i4.1 + IL_002a: add + IL_002b: stind.i4 + IL_002c: ldloca.s V_1 + IL_002e: ldc.i4.1 + IL_002f: newarr "int" + IL_0034: dup + IL_0035: ldc.i4.0 + IL_0036: ldarg.0 + IL_0037: stelem.i4 + IL_0038: call "System.Span..ctor(int[])" + IL_003d: ldloca.s V_1 + IL_003f: ldc.i4.0 + IL_0040: call "ref int System.Span.this[int].get" + IL_0045: ldind.i4 + IL_0046: call "void System.Console.Write(int)" + IL_004b: ret + } + """); + } + + [Fact] + public void Span_SingleElement_TempsAreNotReused_SameBlock() + { + var source = """ + using System; + + class Program + { + static void Main() => M(1); + + static void M(int x) + { + Span y = [x]; + Console.Write(y[0]); + y[0]++; + + Span z = [x]; + Console.Write(z[0]); + } + } + """; + + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 62 (0x3e) + .maxstack 3 + .locals init (System.Span V_0, //y + System.Span V_1, //z + int V_2, + int V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: stloc.0 + IL_000a: ldloca.s V_0 + IL_000c: ldc.i4.0 + IL_000d: call "ref int System.Span.this[int].get" + IL_0012: ldind.i4 + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloca.s V_0 + IL_001a: ldc.i4.0 + IL_001b: call "ref int System.Span.this[int].get" + IL_0020: dup + IL_0021: ldind.i4 + IL_0022: ldc.i4.1 + IL_0023: add + IL_0024: stind.i4 + IL_0025: ldarg.0 + IL_0026: stloc.3 + IL_0027: ldloca.s V_3 + IL_0029: newobj "System.Span..ctor(ref int)" + IL_002e: stloc.1 + IL_002f: ldloca.s V_1 + IL_0031: ldc.i4.0 + IL_0032: call "ref int System.Span.this[int].get" + IL_0037: ldind.i4 + IL_0038: call "void System.Console.Write(int)" + IL_003d: ret + } + """); + + verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 76 (0x4c) + .maxstack 5 + .locals init (System.Span V_0, //y + System.Span V_1) //z + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldarg.0 + IL_000b: stelem.i4 + IL_000c: call "System.Span..ctor(int[])" + IL_0011: ldloca.s V_0 + IL_0013: ldc.i4.0 + IL_0014: call "ref int System.Span.this[int].get" + IL_0019: ldind.i4 + IL_001a: call "void System.Console.Write(int)" + IL_001f: ldloca.s V_0 + IL_0021: ldc.i4.0 + IL_0022: call "ref int System.Span.this[int].get" + IL_0027: dup + IL_0028: ldind.i4 + IL_0029: ldc.i4.1 + IL_002a: add + IL_002b: stind.i4 + IL_002c: ldloca.s V_1 + IL_002e: ldc.i4.1 + IL_002f: newarr "int" + IL_0034: dup + IL_0035: ldc.i4.0 + IL_0036: ldarg.0 + IL_0037: stelem.i4 + IL_0038: call "System.Span..ctor(int[])" + IL_003d: ldloca.s V_1 + IL_003f: ldc.i4.0 + IL_0040: call "ref int System.Span.this[int].get" + IL_0045: ldind.i4 + IL_0046: call "void System.Console.Write(int)" + IL_004b: ret + } + """); + } + + [Fact] + public void ReadOnlySpan_SingleElement() + { + var source = """ + using System; + + class Program + { + static void Main() => M(1); + + static void M(int x) + { + ReadOnlySpan y = [x]; + x++; + Console.Write(y[0]); + Console.Write(x); + } + } + """; + + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 36 (0x24) + .maxstack 2 + .locals init (System.ReadOnlySpan V_0, //y + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloca.s V_1 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly int)" + IL_0009: stloc.0 + IL_000a: ldarg.0 + IL_000b: ldc.i4.1 + IL_000c: add + IL_000d: starg.s V_0 + IL_000f: ldloca.s V_0 + IL_0011: ldc.i4.0 + IL_0012: call "ref readonly int System.ReadOnlySpan.this[int].get" + IL_0017: ldind.i4 + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldarg.0 + IL_001e: call "void System.Console.Write(int)" + IL_0023: ret + } + """); + + verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.M", """ + { + // Code size 43 (0x2b) + .maxstack 5 + .locals init (System.ReadOnlySpan V_0) //y + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldarg.0 + IL_000b: stelem.i4 + IL_000c: call "System.ReadOnlySpan..ctor(int[])" + IL_0011: ldarg.0 + IL_0012: ldc.i4.1 + IL_0013: add + IL_0014: starg.s V_0 + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.0 + IL_0019: call "ref readonly int System.ReadOnlySpan.this[int].get" + IL_001e: ldind.i4 + IL_001f: call "void System.Console.Write(int)" + IL_0024: ldarg.0 + IL_0025: call "void System.Console.Write(int)" + IL_002a: ret + } + """); + } + [CombinatorialData] [Theory] public void SpanArgument_01([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) @@ -20936,81 +21332,48 @@ static void Main() new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Skipped, - symbolValidator: module => - { - if (targetFramework == TargetFramework.Net80) - { - var synthesizedType = module.GlobalNamespace.GetTypeMember("<>y__InlineArray1"); - Assert.Equal("<>y__InlineArray1", synthesizedType.ToTestDisplayString()); - Assert.Equal("<>y__InlineArray1`1", synthesizedType.MetadataName); - } - }, expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.Main", """ { - // Code size 161 (0xa1) + // Code size 87 (0x57) .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3, + .locals init (object V_0, + int? V_1, + int? V_2, + object V_3, System.Span V_4, System.ReadOnlySpan V_5) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_0 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: call "void Program.F1(System.Span)" - IL_0024: ldloca.s V_1 - IL_0026: initobj "<>y__InlineArray1" - IL_002c: ldloca.s V_1 - IL_002e: ldc.i4.0 - IL_002f: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_0034: ldc.i4.2 - IL_0035: newobj "int?..ctor(int)" - IL_003a: stobj "int?" - IL_003f: ldloca.s V_1 - IL_0041: ldc.i4.1 - IL_0042: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_0047: call "void Program.F2(System.ReadOnlySpan)" - IL_004c: ldloca.s V_2 - IL_004e: initobj "<>y__InlineArray1" - IL_0054: ldloca.s V_2 - IL_0056: ldc.i4.0 - IL_0057: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_005c: ldc.i4.3 - IL_005d: newobj "int?..ctor(int)" - IL_0062: stobj "int?" - IL_0067: ldloca.s V_2 - IL_0069: ldc.i4.1 - IL_006a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_006f: stloc.s V_4 - IL_0071: ldloca.s V_4 - IL_0073: call "void Program.F3(in System.Span)" - IL_0078: ldloca.s V_3 - IL_007a: initobj "<>y__InlineArray1" - IL_0080: ldloca.s V_3 - IL_0082: ldc.i4.0 - IL_0083: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0088: ldc.i4.4 - IL_0089: box "int" - IL_008e: stind.ref - IL_008f: ldloca.s V_3 - IL_0091: ldc.i4.1 - IL_0092: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0097: stloc.s V_5 - IL_0099: ldloca.s V_5 - IL_009b: call "void Program.F4(in System.ReadOnlySpan)" - IL_00a0: ret + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: call "void Program.F1(System.Span)" + IL_0013: ldloca.s V_1 + IL_0015: ldc.i4.2 + IL_0016: call "int?..ctor(int)" + IL_001b: ldloca.s V_1 + IL_001d: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0022: call "void Program.F2(System.ReadOnlySpan)" + IL_0027: ldloca.s V_2 + IL_0029: ldc.i4.3 + IL_002a: call "int?..ctor(int)" + IL_002f: ldloca.s V_2 + IL_0031: newobj "System.Span..ctor(ref int?)" + IL_0036: stloc.s V_4 + IL_0038: ldloca.s V_4 + IL_003a: call "void Program.F3(in System.Span)" + IL_003f: ldc.i4.4 + IL_0040: box "int" + IL_0045: stloc.3 + IL_0046: ldloca.s V_3 + IL_0048: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_004d: stloc.s V_5 + IL_004f: ldloca.s V_5 + IL_0051: call "void Program.F4(in System.ReadOnlySpan)" + IL_0056: ret } """); } @@ -21098,65 +21461,41 @@ static void Main() expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 149 (0x95) - .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_0 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: call "S Program.ReturnsStruct(System.Span)" - IL_0024: pop - IL_0025: ldloca.s V_1 - IL_0027: initobj "<>y__InlineArray1" - IL_002d: ldloca.s V_1 - IL_002f: ldc.i4.0 - IL_0030: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0035: ldc.i4.2 - IL_0036: box "int" - IL_003b: stind.ref - IL_003c: ldloca.s V_1 - IL_003e: ldc.i4.1 - IL_003f: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0044: call "R Program.ReturnsRefStruct(System.Span)" - IL_0049: pop - IL_004a: ldloca.s V_2 - IL_004c: initobj "<>y__InlineArray1" - IL_0052: ldloca.s V_2 - IL_0054: ldc.i4.0 - IL_0055: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_005a: ldc.i4.3 - IL_005b: box "int" - IL_0060: stind.ref - IL_0061: ldloca.s V_2 - IL_0063: ldc.i4.1 - IL_0064: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0069: call "ref int Program.ReturnsRef(System.Span)" - IL_006e: pop - IL_006f: ldloca.s V_3 - IL_0071: initobj "<>y__InlineArray1" - IL_0077: ldloca.s V_3 - IL_0079: ldc.i4.0 - IL_007a: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_007f: ldc.i4.4 - IL_0080: box "int" - IL_0085: stind.ref - IL_0086: ldloca.s V_3 - IL_0088: ldc.i4.1 - IL_0089: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_008e: call "ref readonly int Program.ReturnsRefReadOnly(System.Span)" - IL_0093: pop - IL_0094: ret + // Code size 81 (0x51) + .maxstack 1 + .locals init (object V_0, + object V_1, + object V_2, + object V_3) + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: call "S Program.ReturnsStruct(System.Span)" + IL_0013: pop + IL_0014: ldc.i4.2 + IL_0015: box "int" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: newobj "System.Span..ctor(ref object)" + IL_0022: call "R Program.ReturnsRefStruct(System.Span)" + IL_0027: pop + IL_0028: ldc.i4.3 + IL_0029: box "int" + IL_002e: stloc.2 + IL_002f: ldloca.s V_2 + IL_0031: newobj "System.Span..ctor(ref object)" + IL_0036: call "ref int Program.ReturnsRef(System.Span)" + IL_003b: pop + IL_003c: ldc.i4.4 + IL_003d: box "int" + IL_0042: stloc.3 + IL_0043: ldloca.s V_3 + IL_0045: newobj "System.Span..ctor(ref object)" + IL_004a: call "ref readonly int Program.ReturnsRefReadOnly(System.Span)" + IL_004f: pop + IL_0050: ret } """); } @@ -21189,51 +21528,33 @@ static void Main() expectedOutput: IncludeExpectedOutput("[2], [3], [4], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 112 (0x70) - .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.2 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_0 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: call "R Program.ReturnsRefStruct(scoped System.Span)" - IL_0024: pop - IL_0025: ldloca.s V_1 - IL_0027: initobj "<>y__InlineArray1" - IL_002d: ldloca.s V_1 - IL_002f: ldc.i4.0 - IL_0030: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0035: ldc.i4.3 - IL_0036: box "int" - IL_003b: stind.ref - IL_003c: ldloca.s V_1 - IL_003e: ldc.i4.1 - IL_003f: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0044: call "ref int Program.ReturnsRef(scoped System.Span)" - IL_0049: pop - IL_004a: ldloca.s V_2 - IL_004c: initobj "<>y__InlineArray1" - IL_0052: ldloca.s V_2 - IL_0054: ldc.i4.0 - IL_0055: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_005a: ldc.i4.4 - IL_005b: box "int" - IL_0060: stind.ref - IL_0061: ldloca.s V_2 - IL_0063: ldc.i4.1 - IL_0064: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0069: call "ref readonly int Program.ReturnsRefReadOnly(scoped System.Span)" - IL_006e: pop - IL_006f: ret + // Code size 61 (0x3d) + .maxstack 1 + .locals init (object V_0, + object V_1, + object V_2) + IL_0000: ldc.i4.2 + IL_0001: box "int" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: call "R Program.ReturnsRefStruct(scoped System.Span)" + IL_0013: pop + IL_0014: ldc.i4.3 + IL_0015: box "int" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: newobj "System.Span..ctor(ref object)" + IL_0022: call "ref int Program.ReturnsRef(scoped System.Span)" + IL_0027: pop + IL_0028: ldc.i4.4 + IL_0029: box "int" + IL_002e: stloc.2 + IL_002f: ldloca.s V_2 + IL_0031: newobj "System.Span..ctor(ref object)" + IL_0036: call "ref readonly int Program.ReturnsRefReadOnly(scoped System.Span)" + IL_003b: pop + IL_003c: ret } """); } @@ -21318,105 +21639,69 @@ static void Main() expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], [5], [6], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 280 (0x118) + // Code size 160 (0xa0) .maxstack 3 .locals init (S V_0, //s R1 V_1, //r1 R2 V_2, //r2 - <>y__InlineArray1 V_3, - <>y__InlineArray1 V_4, - <>y__InlineArray1 V_5, - <>y__InlineArray1 V_6, - <>y__InlineArray1 V_7, - <>y__InlineArray1 V_8) + int? V_3, + int? V_4, + int? V_5, + int? V_6, + int? V_7, + int? V_8) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: ldloca.s V_3 - IL_000c: initobj "<>y__InlineArray1" + IL_000c: ldc.i4.1 + IL_000d: call "int?..ctor(int)" IL_0012: ldloca.s V_3 - IL_0014: ldc.i4.0 - IL_0015: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_001a: ldc.i4.1 - IL_001b: newobj "int?..ctor(int)" - IL_0020: stobj "int?" - IL_0025: ldloca.s V_3 - IL_0027: ldc.i4.1 - IL_0028: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_002d: call "void S.M(System.ReadOnlySpan)" - IL_0032: ldloca.s V_0 - IL_0034: ldloca.s V_4 - IL_0036: initobj "<>y__InlineArray1" - IL_003c: ldloca.s V_4 - IL_003e: ldc.i4.0 - IL_003f: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_0044: ldc.i4.2 - IL_0045: newobj "int?..ctor(int)" - IL_004a: stobj "int?" - IL_004f: ldloca.s V_4 - IL_0051: ldc.i4.1 - IL_0052: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_0057: ldnull - IL_0058: call "void S.this[System.ReadOnlySpan].set" - IL_005d: ldloca.s V_1 - IL_005f: initobj "R1" - IL_0065: ldloca.s V_1 - IL_0067: ldloca.s V_5 - IL_0069: initobj "<>y__InlineArray1" - IL_006f: ldloca.s V_5 - IL_0071: ldc.i4.0 - IL_0072: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_0077: ldc.i4.3 - IL_0078: newobj "int?..ctor(int)" - IL_007d: stobj "int?" - IL_0082: ldloca.s V_5 - IL_0084: ldc.i4.1 - IL_0085: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_008a: call "void R1.M(System.ReadOnlySpan)" - IL_008f: ldloca.s V_1 - IL_0091: ldloca.s V_6 - IL_0093: initobj "<>y__InlineArray1" - IL_0099: ldloca.s V_6 - IL_009b: ldc.i4.0 - IL_009c: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_00a1: ldc.i4.4 - IL_00a2: newobj "int?..ctor(int)" - IL_00a7: stobj "int?" - IL_00ac: ldloca.s V_6 - IL_00ae: ldc.i4.1 - IL_00af: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_00b4: ldnull - IL_00b5: call "void R1.this[System.ReadOnlySpan].set" - IL_00ba: ldloca.s V_2 - IL_00bc: initobj "R2" - IL_00c2: ldloca.s V_2 - IL_00c4: ldloca.s V_7 - IL_00c6: initobj "<>y__InlineArray1" - IL_00cc: ldloca.s V_7 - IL_00ce: ldc.i4.0 - IL_00cf: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_00d4: ldc.i4.5 - IL_00d5: newobj "int?..ctor(int)" - IL_00da: stobj "int?" - IL_00df: ldloca.s V_7 - IL_00e1: ldc.i4.1 - IL_00e2: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_00e7: call "void R2.M(scoped System.ReadOnlySpan)" - IL_00ec: ldloca.s V_2 - IL_00ee: ldloca.s V_8 - IL_00f0: initobj "<>y__InlineArray1" - IL_00f6: ldloca.s V_8 - IL_00f8: ldc.i4.0 - IL_00f9: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_00fe: ldc.i4.6 - IL_00ff: newobj "int?..ctor(int)" - IL_0104: stobj "int?" - IL_0109: ldloca.s V_8 - IL_010b: ldc.i4.1 - IL_010c: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_0111: ldnull - IL_0112: call "void R2.this[scoped System.ReadOnlySpan].set" - IL_0117: ret + IL_0014: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0019: call "void S.M(System.ReadOnlySpan)" + IL_001e: ldloca.s V_0 + IL_0020: ldloca.s V_4 + IL_0022: ldc.i4.2 + IL_0023: call "int?..ctor(int)" + IL_0028: ldloca.s V_4 + IL_002a: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_002f: ldnull + IL_0030: call "void S.this[System.ReadOnlySpan].set" + IL_0035: ldloca.s V_1 + IL_0037: initobj "R1" + IL_003d: ldloca.s V_1 + IL_003f: ldloca.s V_5 + IL_0041: ldc.i4.3 + IL_0042: call "int?..ctor(int)" + IL_0047: ldloca.s V_5 + IL_0049: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_004e: call "void R1.M(System.ReadOnlySpan)" + IL_0053: ldloca.s V_1 + IL_0055: ldloca.s V_6 + IL_0057: ldc.i4.4 + IL_0058: call "int?..ctor(int)" + IL_005d: ldloca.s V_6 + IL_005f: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0064: ldnull + IL_0065: call "void R1.this[System.ReadOnlySpan].set" + IL_006a: ldloca.s V_2 + IL_006c: initobj "R2" + IL_0072: ldloca.s V_2 + IL_0074: ldloca.s V_7 + IL_0076: ldc.i4.5 + IL_0077: call "int?..ctor(int)" + IL_007c: ldloca.s V_7 + IL_007e: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0083: call "void R2.M(scoped System.ReadOnlySpan)" + IL_0088: ldloca.s V_2 + IL_008a: ldloca.s V_8 + IL_008c: ldc.i4.6 + IL_008d: call "int?..ctor(int)" + IL_0092: ldloca.s V_8 + IL_0094: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0099: ldnull + IL_009a: call "void R2.this[scoped System.ReadOnlySpan].set" + IL_009f: ret } """); } @@ -21456,73 +21741,49 @@ static void Main() expectedOutput: IncludeExpectedOutput("[3], [4], [5], [6], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 187 (0xbb) + // Code size 107 (0x6b) .maxstack 3 .locals init (R1 V_0, //r1 R2 V_1, //r2 - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3, - <>y__InlineArray1 V_4, - <>y__InlineArray1 V_5) + int? V_2, + int? V_3, + int? V_4, + int? V_5) IL_0000: ldloca.s V_0 IL_0002: initobj "R1" IL_0008: ldloca.s V_0 IL_000a: ldloca.s V_2 - IL_000c: initobj "<>y__InlineArray1" + IL_000c: ldc.i4.3 + IL_000d: call "int?..ctor(int)" IL_0012: ldloca.s V_2 - IL_0014: ldc.i4.0 - IL_0015: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_001a: ldc.i4.3 - IL_001b: newobj "int?..ctor(int)" - IL_0020: stobj "int?" - IL_0025: ldloca.s V_2 - IL_0027: ldc.i4.1 - IL_0028: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_002d: call "void R1.M(System.ReadOnlySpan)" - IL_0032: ldloca.s V_0 - IL_0034: ldloca.s V_3 - IL_0036: initobj "<>y__InlineArray1" - IL_003c: ldloca.s V_3 - IL_003e: ldc.i4.0 - IL_003f: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_0044: ldc.i4.4 - IL_0045: newobj "int?..ctor(int)" - IL_004a: stobj "int?" - IL_004f: ldloca.s V_3 - IL_0051: ldc.i4.1 - IL_0052: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_0057: call "object R1.this[System.ReadOnlySpan].get" - IL_005c: pop - IL_005d: ldloca.s V_1 - IL_005f: initobj "R2" - IL_0065: ldloca.s V_1 - IL_0067: ldloca.s V_4 - IL_0069: initobj "<>y__InlineArray1" - IL_006f: ldloca.s V_4 - IL_0071: ldc.i4.0 - IL_0072: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_0077: ldc.i4.5 - IL_0078: newobj "int?..ctor(int)" - IL_007d: stobj "int?" - IL_0082: ldloca.s V_4 - IL_0084: ldc.i4.1 - IL_0085: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_008a: call "readonly void R2.M(System.ReadOnlySpan)" - IL_008f: ldloca.s V_1 - IL_0091: ldloca.s V_5 - IL_0093: initobj "<>y__InlineArray1" - IL_0099: ldloca.s V_5 - IL_009b: ldc.i4.0 - IL_009c: call "ref int? .InlineArrayElementRef<<>y__InlineArray1, int?>(ref <>y__InlineArray1, int)" - IL_00a1: ldc.i4.6 - IL_00a2: newobj "int?..ctor(int)" - IL_00a7: stobj "int?" - IL_00ac: ldloca.s V_5 - IL_00ae: ldc.i4.1 - IL_00af: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int?>(in <>y__InlineArray1, int)" - IL_00b4: call "readonly object R2.this[System.ReadOnlySpan].get" - IL_00b9: pop - IL_00ba: ret + IL_0014: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0019: call "void R1.M(System.ReadOnlySpan)" + IL_001e: ldloca.s V_0 + IL_0020: ldloca.s V_3 + IL_0022: ldc.i4.4 + IL_0023: call "int?..ctor(int)" + IL_0028: ldloca.s V_3 + IL_002a: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_002f: call "object R1.this[System.ReadOnlySpan].get" + IL_0034: pop + IL_0035: ldloca.s V_1 + IL_0037: initobj "R2" + IL_003d: ldloca.s V_1 + IL_003f: ldloca.s V_4 + IL_0041: ldc.i4.5 + IL_0042: call "int?..ctor(int)" + IL_0047: ldloca.s V_4 + IL_0049: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_004e: call "readonly void R2.M(System.ReadOnlySpan)" + IL_0053: ldloca.s V_1 + IL_0055: ldloca.s V_5 + IL_0057: ldc.i4.6 + IL_0058: call "int?..ctor(int)" + IL_005d: ldloca.s V_5 + IL_005f: newobj "System.ReadOnlySpan..ctor(ref readonly int?)" + IL_0064: call "readonly object R2.this[System.ReadOnlySpan].get" + IL_0069: pop + IL_006a: ret } """); } @@ -21550,52 +21811,34 @@ static void Main() expectedOutput: IncludeExpectedOutput("[1], [3], [2], [4], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 113 (0x71) - .maxstack 3 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref int .InlineArrayElementRef<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_0010: ldloca.s V_1 - IL_0012: initobj "<>y__InlineArray1" - IL_0018: ldloca.s V_1 - IL_001a: ldc.i4.0 - IL_001b: call "ref int .InlineArrayElementRef<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_0020: ldc.i4.1 - IL_0021: stind.i4 - IL_0022: ldloca.s V_1 - IL_0024: ldc.i4.1 - IL_0025: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_002a: call "int Program.F1(System.Span)" - IL_002f: ldc.i4.2 - IL_0030: add - IL_0031: stind.i4 - IL_0032: ldloca.s V_0 - IL_0034: ldc.i4.1 - IL_0035: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_003a: call "int Program.F1(System.Span)" - IL_003f: pop - IL_0040: ldloca.s V_2 - IL_0042: initobj "<>y__InlineArray1" - IL_0048: ldloca.s V_2 - IL_004a: ldc.i4.0 - IL_004b: call "ref int .InlineArrayElementRef<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_0050: ldtoken ".__StaticArrayInitTypeSize=4_Align=4 .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" - IL_0055: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" - IL_005a: call "int Program.F2(System.ReadOnlySpan)" - IL_005f: ldc.i4.2 - IL_0060: add - IL_0061: stind.i4 - IL_0062: ldloca.s V_2 - IL_0064: ldc.i4.1 - IL_0065: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, int>(in <>y__InlineArray1, int)" - IL_006a: call "int Program.F2(System.ReadOnlySpan)" - IL_006f: pop - IL_0070: ret + // Code size 62 (0x3e) + .maxstack 2 + .locals init (int V_0, + int V_1, + int V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: call "int Program.F1(System.Span)" + IL_000e: ldc.i4.2 + IL_000f: add + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: newobj "System.Span..ctor(ref int)" + IL_0018: call "int Program.F1(System.Span)" + IL_001d: pop + IL_001e: ldtoken ".__StaticArrayInitTypeSize=4_Align=4 .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" + IL_0023: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" + IL_0028: call "int Program.F2(System.ReadOnlySpan)" + IL_002d: ldc.i4.2 + IL_002e: add + IL_002f: stloc.2 + IL_0030: ldloca.s V_2 + IL_0032: newobj "System.ReadOnlySpan..ctor(ref readonly int)" + IL_0037: call "int Program.F2(System.ReadOnlySpan)" + IL_003c: pop + IL_003d: ret } """); } @@ -21633,67 +21876,43 @@ static ReadOnlySpan F2(scoped ReadOnlySpan x, ReadOnlySpan y) expectedOutput: IncludeExpectedOutput("[2], [1], [4], [3], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 145 (0x91) + // Code size 77 (0x4d) .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3, + .locals init (object V_0, + object V_1, + object V_2, + object V_3, System.Span V_4, System.ReadOnlySpan V_5) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: stloc.s V_4 + IL_0010: ldc.i4.2 IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_0 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: stloc.s V_4 - IL_0021: ldloca.s V_1 - IL_0023: initobj "<>y__InlineArray1" - IL_0029: ldloca.s V_1 - IL_002b: ldc.i4.0 - IL_002c: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0031: ldc.i4.2 - IL_0032: box "int" - IL_0037: stind.ref - IL_0038: ldloca.s V_1 - IL_003a: ldc.i4.1 - IL_003b: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0040: ldloc.s V_4 - IL_0042: call "System.Span Program.F1(System.Span, scoped System.Span)" - IL_0047: pop - IL_0048: ldloca.s V_2 - IL_004a: initobj "<>y__InlineArray1" - IL_0050: ldloca.s V_2 - IL_0052: ldc.i4.0 - IL_0053: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0058: ldc.i4.3 - IL_0059: box "int" - IL_005e: stind.ref - IL_005f: ldloca.s V_2 - IL_0061: ldc.i4.1 - IL_0062: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0067: stloc.s V_5 - IL_0069: ldloca.s V_3 - IL_006b: initobj "<>y__InlineArray1" - IL_0071: ldloca.s V_3 - IL_0073: ldc.i4.0 - IL_0074: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0079: ldc.i4.4 - IL_007a: box "int" - IL_007f: stind.ref - IL_0080: ldloca.s V_3 - IL_0082: ldc.i4.1 - IL_0083: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0088: ldloc.s V_5 - IL_008a: call "System.ReadOnlySpan Program.F2(scoped System.ReadOnlySpan, System.ReadOnlySpan)" - IL_008f: pop - IL_0090: ret + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: newobj "System.Span..ctor(ref object)" + IL_001e: ldloc.s V_4 + IL_0020: call "System.Span Program.F1(System.Span, scoped System.Span)" + IL_0025: pop + IL_0026: ldc.i4.3 + IL_0027: box "int" + IL_002c: stloc.2 + IL_002d: ldloca.s V_2 + IL_002f: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0034: stloc.s V_5 + IL_0036: ldc.i4.4 + IL_0037: box "int" + IL_003c: stloc.3 + IL_003d: ldloca.s V_3 + IL_003f: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0044: ldloc.s V_5 + IL_0046: call "System.ReadOnlySpan Program.F2(scoped System.ReadOnlySpan, System.ReadOnlySpan)" + IL_004b: pop + IL_004c: ret } """); } @@ -21947,48 +22166,36 @@ static object[] F2() expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.F1", """ { - // Code size 40 (0x28) - .maxstack 2 - .locals init (System.Span V_0, //s1 - <>y__InlineArray1 V_1) - IL_0000: ldloca.s V_1 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_1 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_1 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: stloc.0 - IL_0020: ldloca.s V_0 - IL_0022: call "object[] System.Span.ToArray()" - IL_0027: ret + // Code size 23 (0x17) + .maxstack 1 + .locals init (System.Span V_0, //s1 + object V_1) + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.1 + IL_0007: ldloca.s V_1 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "object[] System.Span.ToArray()" + IL_0016: ret } """); verifier.VerifyIL("Program.F2", """ { - // Code size 40 (0x28) - .maxstack 2 - .locals init (System.ReadOnlySpan V_0, //s2 - <>y__InlineArray1 V_1) - IL_0000: ldloca.s V_1 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_1 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.2 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_1 - IL_0019: ldc.i4.1 - IL_001a: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_001f: stloc.0 - IL_0020: ldloca.s V_0 - IL_0022: call "object[] System.ReadOnlySpan.ToArray()" - IL_0027: ret + // Code size 23 (0x17) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0, //s2 + object V_1) + IL_0000: ldc.i4.2 + IL_0001: box "int" + IL_0006: stloc.1 + IL_0007: ldloca.s V_1 + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "object[] System.ReadOnlySpan.ToArray()" + IL_0016: ret } """); } @@ -22021,41 +22228,29 @@ static void Main() { verifier.VerifyIL("Program.Main", """ { - // Code size 79 (0x4f) - .maxstack 2 + // Code size 45 (0x2d) + .maxstack 1 .locals init (System.Span V_0, //x System.ReadOnlySpan V_1, //y - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3) - IL_0000: ldloca.s V_2 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_2 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_2 - IL_0019: ldc.i4.1 - IL_001a: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_001f: stloc.0 - IL_0020: ldloca.s V_3 - IL_0022: initobj "<>y__InlineArray1" - IL_0028: ldloca.s V_3 - IL_002a: ldc.i4.0 - IL_002b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0030: ldc.i4.2 - IL_0031: box "int" - IL_0036: stind.ref - IL_0037: ldloca.s V_3 - IL_0039: ldc.i4.1 - IL_003a: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_003f: stloc.1 - IL_0040: ldloca.s V_0 - IL_0042: call "void CollectionExtensions.Report(in System.Span)" - IL_0047: ldloca.s V_1 - IL_0049: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_004e: ret + object V_2, + object V_3) + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.2 + IL_0007: ldloca.s V_2 + IL_0009: newobj "System.Span..ctor(ref object)" + IL_000e: stloc.0 + IL_000f: ldc.i4.2 + IL_0010: box "int" + IL_0015: stloc.3 + IL_0016: ldloca.s V_3 + IL_0018: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_001d: stloc.1 + IL_001e: ldloca.s V_0 + IL_0020: call "void CollectionExtensions.Report(in System.Span)" + IL_0025: ldloca.s V_1 + IL_0027: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_002c: ret } """); } @@ -22149,49 +22344,37 @@ static void Main() expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.Main", """ { - // Code size 117 (0x75) - .maxstack 3 + // Code size 83 (0x53) + .maxstack 2 .locals init (R V_0, //x R V_1, //y - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3) + object V_2, + object V_3) IL_0000: ldloca.s V_0 IL_0002: initobj "R" IL_0008: ldloca.s V_1 IL_000a: initobj "R" IL_0010: ldloca.s V_0 - IL_0012: ldloca.s V_2 - IL_0014: initobj "<>y__InlineArray1" - IL_001a: ldloca.s V_2 - IL_001c: ldc.i4.0 - IL_001d: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0022: ldc.i4.1 - IL_0023: box "int" - IL_0028: stind.ref - IL_0029: ldloca.s V_2 - IL_002b: ldc.i4.1 - IL_002c: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0031: stfld "System.ReadOnlySpan R.F" - IL_0036: ldloca.s V_1 - IL_0038: ldloca.s V_3 - IL_003a: initobj "<>y__InlineArray1" - IL_0040: ldloca.s V_3 - IL_0042: ldc.i4.0 - IL_0043: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0048: ldc.i4.2 - IL_0049: box "int" - IL_004e: stind.ref - IL_004f: ldloca.s V_3 - IL_0051: ldc.i4.1 - IL_0052: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0057: stfld "System.ReadOnlySpan R.F" - IL_005c: ldloca.s V_0 - IL_005e: ldflda "System.ReadOnlySpan R.F" - IL_0063: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0068: ldloca.s V_1 - IL_006a: ldflda "System.ReadOnlySpan R.F" - IL_006f: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0074: ret + IL_0012: ldc.i4.1 + IL_0013: box "int" + IL_0018: stloc.2 + IL_0019: ldloca.s V_2 + IL_001b: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0020: stfld "System.ReadOnlySpan R.F" + IL_0025: ldloca.s V_1 + IL_0027: ldc.i4.2 + IL_0028: box "int" + IL_002d: stloc.3 + IL_002e: ldloca.s V_3 + IL_0030: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0035: stfld "System.ReadOnlySpan R.F" + IL_003a: ldloca.s V_0 + IL_003c: ldflda "System.ReadOnlySpan R.F" + IL_0041: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0046: ldloca.s V_1 + IL_0048: ldflda "System.ReadOnlySpan R.F" + IL_004d: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0052: ret } """); } @@ -22451,64 +22634,40 @@ static void F(bool b) { verifier.VerifyIL("Program.F", """ { - // Code size 134 (0x86) - .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_0 - IL_0019: ldc.i4.1 - IL_001a: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_001f: pop - IL_0020: ldarg.0 - IL_0021: brfalse.s IL_0045 - IL_0023: ldloca.s V_1 - IL_0025: initobj "<>y__InlineArray1" - IL_002b: ldloca.s V_1 - IL_002d: ldc.i4.0 - IL_002e: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0033: ldc.i4.2 - IL_0034: box "int" - IL_0039: stind.ref - IL_003a: ldloca.s V_1 - IL_003c: ldc.i4.1 - IL_003d: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0042: pop - IL_0043: br.s IL_0065 - IL_0045: ldloca.s V_2 - IL_0047: initobj "<>y__InlineArray1" - IL_004d: ldloca.s V_2 - IL_004f: ldc.i4.0 - IL_0050: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0055: ldc.i4.3 - IL_0056: box "int" - IL_005b: stind.ref - IL_005c: ldloca.s V_2 - IL_005e: ldc.i4.1 - IL_005f: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0064: pop - IL_0065: ldloca.s V_3 - IL_0067: initobj "<>y__InlineArray1" - IL_006d: ldloca.s V_3 - IL_006f: ldc.i4.0 - IL_0070: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0075: ldc.i4.4 - IL_0076: box "int" - IL_007b: stind.ref - IL_007c: ldloca.s V_3 - IL_007e: ldc.i4.1 - IL_007f: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0084: pop - IL_0085: ret + // Code size 66 (0x42) + .maxstack 1 + .locals init (object V_0, + object V_1, + object V_2, + object V_3) + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_000e: pop + IL_000f: ldarg.0 + IL_0010: brfalse.s IL_0023 + IL_0012: ldc.i4.2 + IL_0013: box "int" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0020: pop + IL_0021: br.s IL_0032 + IL_0023: ldc.i4.3 + IL_0024: box "int" + IL_0029: stloc.2 + IL_002a: ldloca.s V_2 + IL_002c: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0031: pop + IL_0032: ldc.i4.4 + IL_0033: box "int" + IL_0038: stloc.3 + IL_0039: ldloca.s V_3 + IL_003b: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0040: pop + IL_0041: ret } """); } @@ -22766,25 +22925,19 @@ void A2() expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], [1], ")); verifier.VerifyIL("Program.<>c__DisplayClass1_0.g__A2|1()", """ { - // Code size 44 (0x2c) - .maxstack 2 + // Code size 23 (0x17) + .maxstack 1 .locals init (System.Span V_0, //s3 - <>y__InlineArray1 V_1) - IL_0000: ldloca.s V_1 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_1 - IL_000a: ldc.i4.0 - IL_000b: call "ref T .InlineArrayElementRef<<>y__InlineArray1, T>(ref <>y__InlineArray1, int)" - IL_0010: ldarg.0 - IL_0011: ldfld "T Program.<>c__DisplayClass1_0.z" - IL_0016: stobj "T" - IL_001b: ldloca.s V_1 - IL_001d: ldc.i4.1 - IL_001e: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, T>(ref <>y__InlineArray1, int)" - IL_0023: stloc.0 - IL_0024: ldloca.s V_0 - IL_0026: call "void CollectionExtensions.Report(in System.Span)" - IL_002b: ret + T V_1) + IL_0000: ldarg.0 + IL_0001: ldfld "T Program.<>c__DisplayClass1_0.z" + IL_0006: stloc.1 + IL_0007: ldloca.s V_1 + IL_0009: newobj "System.Span..ctor(ref T)" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "void CollectionExtensions.Report(in System.Span)" + IL_0016: ret } """); } @@ -22826,10 +22979,10 @@ static void Main() expectedOutput: IncludeExpectedOutput("[b], [a], ")); verifier.VerifyIL("C.<>c.<.ctor>b__1_0(T, T)", """ { - // Code size 76 (0x4c) + // Code size 55 (0x37) .maxstack 2 .locals init (System.ReadOnlySpan V_0, //r1 - <>y__InlineArray1 V_1) + T V_1) IL_0000: ldsfld "System.Action C.<>c.<>9__1_1" IL_0005: dup IL_0006: brtrue.s IL_001f @@ -22841,20 +22994,14 @@ .locals init (System.ReadOnlySpan V_0, //r1 IL_001a: stsfld "System.Action C.<>c.<>9__1_1" IL_001f: ldarg.2 IL_0020: callvirt "void System.Action.Invoke(T)" - IL_0025: ldloca.s V_1 - IL_0027: initobj "<>y__InlineArray1" - IL_002d: ldloca.s V_1 - IL_002f: ldc.i4.0 - IL_0030: call "ref T .InlineArrayElementRef<<>y__InlineArray1, T>(ref <>y__InlineArray1, int)" - IL_0035: ldarg.1 - IL_0036: stobj "T" - IL_003b: ldloca.s V_1 - IL_003d: ldc.i4.1 - IL_003e: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, T>(in <>y__InlineArray1, int)" - IL_0043: stloc.0 - IL_0044: ldloca.s V_0 - IL_0046: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_004b: ret + IL_0025: ldarg.1 + IL_0026: stloc.1 + IL_0027: ldloca.s V_1 + IL_0029: newobj "System.ReadOnlySpan..ctor(ref readonly T)" + IL_002e: stloc.0 + IL_002f: ldloca.s V_0 + IL_0031: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0036: ret } """); } @@ -22883,60 +23030,48 @@ static void Main() var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, - verify: Verification.Fails, + verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], Disposed, ")); verifier.VerifyIL("Program.Main", """ { - // Code size 97 (0x61) - .maxstack 2 + // Code size 64 (0x40) + .maxstack 1 .locals init (System.ReadOnlySpan V_0, //x Disposable V_1, //d System.ReadOnlySpan V_2, //y - <>y__InlineArray1 V_3, - <>y__InlineArray1 V_4) - IL_0000: ldloca.s V_3 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_3 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: box "int" - IL_0016: stind.ref - IL_0017: ldloca.s V_3 - IL_0019: ldc.i4.1 - IL_001a: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_001f: stloc.0 - IL_0020: newobj "Disposable..ctor()" - IL_0025: stloc.1 + object V_3, + object V_4) + IL_0000: ldc.i4.1 + IL_0001: box "int" + IL_0006: stloc.3 + IL_0007: ldloca.s V_3 + IL_0009: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_000e: stloc.0 + IL_000f: newobj "Disposable..ctor()" + IL_0014: stloc.1 .try { - IL_0026: ldloca.s V_4 - IL_0028: initobj "<>y__InlineArray1" - IL_002e: ldloca.s V_4 - IL_0030: ldc.i4.0 - IL_0031: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0036: ldc.i4.2 - IL_0037: box "int" - IL_003c: stind.ref - IL_003d: ldloca.s V_4 - IL_003f: ldc.i4.1 - IL_0040: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_0045: stloc.2 - IL_0046: ldloca.s V_0 - IL_0048: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_004d: ldloca.s V_2 - IL_004f: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0054: leave.s IL_0060 + IL_0015: ldc.i4.2 + IL_0016: box "int" + IL_001b: stloc.s V_4 + IL_001d: ldloca.s V_4 + IL_001f: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_0024: stloc.2 + IL_0025: ldloca.s V_0 + IL_0027: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_002c: ldloca.s V_2 + IL_002e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0033: leave.s IL_003f } finally { - IL_0056: ldloc.1 - IL_0057: brfalse.s IL_005f - IL_0059: ldloc.1 - IL_005a: callvirt "void System.IDisposable.Dispose()" - IL_005f: endfinally + IL_0035: ldloc.1 + IL_0036: brfalse.s IL_003e + IL_0038: ldloc.1 + IL_0039: callvirt "void System.IDisposable.Dispose()" + IL_003e: endfinally } - IL_0060: ret + IL_003f: ret } """); } @@ -23326,59 +23461,35 @@ static void Report(ReadOnlySpan s) """)); verifier.VerifyIL("Program.Main", """ { - // Code size 135 (0x87) - .maxstack 2 - .locals init (<>y__InlineArray1 V_0, - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, - <>y__InlineArray1 V_3) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref object .InlineArrayElementRef<<>y__InlineArray1, object>(ref <>y__InlineArray1, int)" - IL_0010: ldstr "1" - IL_0015: stind.ref - IL_0016: ldloca.s V_0 - IL_0018: ldc.i4.1 - IL_0019: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, object>(in <>y__InlineArray1, int)" - IL_001e: call "void Program.Report(System.ReadOnlySpan)" - IL_0023: ldloca.s V_1 - IL_0025: initobj "<>y__InlineArray1" - IL_002b: ldloca.s V_1 - IL_002d: ldc.i4.0 - IL_002e: call "ref string .InlineArrayElementRef<<>y__InlineArray1, string>(ref <>y__InlineArray1, int)" - IL_0033: ldstr "2" - IL_0038: stind.ref - IL_0039: ldloca.s V_1 - IL_003b: ldc.i4.1 - IL_003c: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, string>(in <>y__InlineArray1, int)" - IL_0041: call "void Program.Report(System.ReadOnlySpan)" - IL_0046: ldloca.s V_2 - IL_0048: initobj "<>y__InlineArray1" - IL_004e: ldloca.s V_2 - IL_0050: ldc.i4.0 - IL_0051: call "ref nint .InlineArrayElementRef<<>y__InlineArray1, nint>(ref <>y__InlineArray1, int)" - IL_0056: ldc.i4.3 - IL_0057: conv.i - IL_0058: stind.i - IL_0059: ldloca.s V_2 - IL_005b: ldc.i4.1 - IL_005c: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, nint>(in <>y__InlineArray1, int)" - IL_0061: call "void Program.Report(System.ReadOnlySpan)" - IL_0066: ldloca.s V_3 - IL_0068: initobj "<>y__InlineArray1" - IL_006e: ldloca.s V_3 - IL_0070: ldc.i4.0 - IL_0071: call "ref nuint .InlineArrayElementRef<<>y__InlineArray1, nuint>(ref <>y__InlineArray1, int)" - IL_0076: ldc.i4.4 - IL_0077: conv.i - IL_0078: stind.i - IL_0079: ldloca.s V_3 - IL_007b: ldc.i4.1 - IL_007c: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, nuint>(in <>y__InlineArray1, int)" - IL_0081: call "void Program.Report(System.ReadOnlySpan)" - IL_0086: ret + // Code size 67 (0x43) + .maxstack 1 + .locals init (object V_0, + string V_1, + nint V_2, + nuint V_3) + IL_0000: ldstr "1" + IL_0005: stloc.0 + IL_0006: ldloca.s V_0 + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly object)" + IL_000d: call "void Program.Report(System.ReadOnlySpan)" + IL_0012: ldstr "2" + IL_0017: stloc.1 + IL_0018: ldloca.s V_1 + IL_001a: newobj "System.ReadOnlySpan..ctor(ref readonly string)" + IL_001f: call "void Program.Report(System.ReadOnlySpan)" + IL_0024: ldc.i4.3 + IL_0025: conv.i + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: newobj "System.ReadOnlySpan..ctor(ref readonly nint)" + IL_002e: call "void Program.Report(System.ReadOnlySpan)" + IL_0033: ldc.i4.4 + IL_0034: conv.i + IL_0035: stloc.3 + IL_0036: ldloca.s V_3 + IL_0038: newobj "System.ReadOnlySpan..ctor(ref readonly nuint)" + IL_003d: call "void Program.Report(System.ReadOnlySpan)" + IL_0042: ret } """); } @@ -28675,29 +28786,23 @@ .maxstack 4 verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { - // Code size 44 (0x2c) + // Code size 27 (0x1b) .maxstack 4 - .locals init (<>y__InlineArray1 V_0) - IL_0000: ldloca.s V_0 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call "ref int .InlineArrayElementRef<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_0010: ldc.i4.1 - IL_0011: stind.i4 - IL_0012: ldloca.s V_0 - IL_0014: ldc.i4.1 - IL_0015: call "System.Span .InlineArrayAsSpan<<>y__InlineArray1, int>(ref <>y__InlineArray1, int)" - IL_001a: pop - IL_001b: ldc.i4.1 - IL_001c: newarr "int" - IL_0021: dup - IL_0022: ldc.i4.0 - IL_0023: ldc.i4.1 - IL_0024: stelem.i4 - IL_0025: call "System.Span System.Span.op_Implicit(int[])" - IL_002a: pop - IL_002b: ret + .locals init (int V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: pop + IL_000a: ldc.i4.1 + IL_000b: newarr "int" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.1 + IL_0013: stelem.i4 + IL_0014: call "System.Span System.Span.op_Implicit(int[])" + IL_0019: pop + IL_001a: ret } """); } @@ -31576,92 +31681,80 @@ class D : C { } verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 185 (0xb9) + // Code size 151 (0x97) .maxstack 3 .locals init (System.ReadOnlySpan V_0, //li1 - <>y__InlineArray1 V_1, - <>y__InlineArray1 V_2, + D V_1, + D V_2, System.ReadOnlySpan V_3, System.ReadOnlySpan V_4, int V_5, C[] V_6, System.ReadOnlySpan.Enumerator V_7, D V_8) - IL_0000: ldloca.s V_1 - IL_0002: initobj "<>y__InlineArray1" - IL_0008: ldloca.s V_1 - IL_000a: ldc.i4.0 - IL_000b: call "ref D .InlineArrayElementRef<<>y__InlineArray1, D>(ref <>y__InlineArray1, int)" - IL_0010: newobj "D..ctor()" - IL_0015: stind.ref - IL_0016: ldloca.s V_1 - IL_0018: ldc.i4.1 - IL_0019: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, D>(in <>y__InlineArray1, int)" - IL_001e: stloc.0 - IL_001f: ldloca.s V_2 - IL_0021: initobj "<>y__InlineArray1" - IL_0027: ldloca.s V_2 - IL_0029: ldc.i4.0 - IL_002a: call "ref D .InlineArrayElementRef<<>y__InlineArray1, D>(ref <>y__InlineArray1, int)" - IL_002f: newobj "D..ctor()" - IL_0034: stind.ref - IL_0035: ldloca.s V_2 - IL_0037: ldc.i4.1 - IL_0038: call "System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray1, D>(in <>y__InlineArray1, int)" - IL_003d: ldloc.0 - IL_003e: stloc.3 - IL_003f: stloc.s V_4 - IL_0041: ldc.i4.0 - IL_0042: stloc.s V_5 - IL_0044: ldloca.s V_3 - IL_0046: call "int System.ReadOnlySpan.Length.get" - IL_004b: ldloca.s V_4 - IL_004d: call "int System.ReadOnlySpan.Length.get" - IL_0052: add - IL_0053: newarr "C" - IL_0058: stloc.s V_6 - IL_005a: ldloca.s V_3 - IL_005c: call "System.ReadOnlySpan.Enumerator System.ReadOnlySpan.GetEnumerator()" - IL_0061: stloc.s V_7 - IL_0063: br.s IL_007c - IL_0065: ldloca.s V_7 - IL_0067: call "ref readonly D System.ReadOnlySpan.Enumerator.Current.get" - IL_006c: ldind.ref - IL_006d: stloc.s V_8 - IL_006f: ldloc.s V_6 - IL_0071: ldloc.s V_5 - IL_0073: ldloc.s V_8 - IL_0075: stelem.ref - IL_0076: ldloc.s V_5 - IL_0078: ldc.i4.1 - IL_0079: add - IL_007a: stloc.s V_5 - IL_007c: ldloca.s V_7 - IL_007e: call "bool System.ReadOnlySpan.Enumerator.MoveNext()" - IL_0083: brtrue.s IL_0065 - IL_0085: ldloca.s V_4 - IL_0087: call "System.ReadOnlySpan.Enumerator System.ReadOnlySpan.GetEnumerator()" - IL_008c: stloc.s V_7 - IL_008e: br.s IL_00a7 - IL_0090: ldloca.s V_7 - IL_0092: call "ref readonly D System.ReadOnlySpan.Enumerator.Current.get" - IL_0097: ldind.ref - IL_0098: stloc.s V_8 - IL_009a: ldloc.s V_6 - IL_009c: ldloc.s V_5 - IL_009e: ldloc.s V_8 - IL_00a0: stelem.ref - IL_00a1: ldloc.s V_5 - IL_00a3: ldc.i4.1 - IL_00a4: add - IL_00a5: stloc.s V_5 - IL_00a7: ldloca.s V_7 - IL_00a9: call "bool System.ReadOnlySpan.Enumerator.MoveNext()" - IL_00ae: brtrue.s IL_0090 - IL_00b0: ldloc.s V_6 - IL_00b2: ldc.i4.0 - IL_00b3: call "void CollectionExtensions.Report(object, bool)" - IL_00b8: ret + IL_0000: newobj "D..ctor()" + IL_0005: stloc.1 + IL_0006: ldloca.s V_1 + IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly D)" + IL_000d: stloc.0 + IL_000e: newobj "D..ctor()" + IL_0013: stloc.2 + IL_0014: ldloca.s V_2 + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly D)" + IL_001b: ldloc.0 + IL_001c: stloc.3 + IL_001d: stloc.s V_4 + IL_001f: ldc.i4.0 + IL_0020: stloc.s V_5 + IL_0022: ldloca.s V_3 + IL_0024: call "int System.ReadOnlySpan.Length.get" + IL_0029: ldloca.s V_4 + IL_002b: call "int System.ReadOnlySpan.Length.get" + IL_0030: add + IL_0031: newarr "C" + IL_0036: stloc.s V_6 + IL_0038: ldloca.s V_3 + IL_003a: call "System.ReadOnlySpan.Enumerator System.ReadOnlySpan.GetEnumerator()" + IL_003f: stloc.s V_7 + IL_0041: br.s IL_005a + IL_0043: ldloca.s V_7 + IL_0045: call "ref readonly D System.ReadOnlySpan.Enumerator.Current.get" + IL_004a: ldind.ref + IL_004b: stloc.s V_8 + IL_004d: ldloc.s V_6 + IL_004f: ldloc.s V_5 + IL_0051: ldloc.s V_8 + IL_0053: stelem.ref + IL_0054: ldloc.s V_5 + IL_0056: ldc.i4.1 + IL_0057: add + IL_0058: stloc.s V_5 + IL_005a: ldloca.s V_7 + IL_005c: call "bool System.ReadOnlySpan.Enumerator.MoveNext()" + IL_0061: brtrue.s IL_0043 + IL_0063: ldloca.s V_4 + IL_0065: call "System.ReadOnlySpan.Enumerator System.ReadOnlySpan.GetEnumerator()" + IL_006a: stloc.s V_7 + IL_006c: br.s IL_0085 + IL_006e: ldloca.s V_7 + IL_0070: call "ref readonly D System.ReadOnlySpan.Enumerator.Current.get" + IL_0075: ldind.ref + IL_0076: stloc.s V_8 + IL_0078: ldloc.s V_6 + IL_007a: ldloc.s V_5 + IL_007c: ldloc.s V_8 + IL_007e: stelem.ref + IL_007f: ldloc.s V_5 + IL_0081: ldc.i4.1 + IL_0082: add + IL_0083: stloc.s V_5 + IL_0085: ldloca.s V_7 + IL_0087: call "bool System.ReadOnlySpan.Enumerator.MoveNext()" + IL_008c: brtrue.s IL_006e + IL_008e: ldloc.s V_6 + IL_0090: ldc.i4.0 + IL_0091: call "void CollectionExtensions.Report(object, bool)" + IL_0096: ret } """); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index 1963a7532e9f2..ef624a34bb1dc 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -4691,9 +4691,12 @@ static async Task FromResult(T r) "; var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( - // (24,22): error CS4007: 'await' cannot be used in an expression containing the type 'System.ReadOnlySpan>' - // [Get01()][await FromResult(Get02(x))]; - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await FromResult(Get02(x))").WithArguments("System.ReadOnlySpan>").WithLocation(24, 22) + // (20,12): error CS4007: Instance of type 'System.ReadOnlySpan>' cannot be preserved across 'await' or 'yield' boundary. + // => MemoryMarshal.CreateReadOnlySpan( + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As>, Buffer10>( + ref Unsafe.AsRef(in GetC(x).F)), + 10)").WithArguments("System.ReadOnlySpan>").WithLocation(20, 12) ); } @@ -4743,9 +4746,12 @@ static async Task FromResult(T r) "; var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( - // (24,13): error CS4007: 'await' cannot be used in an expression containing the type 'System.ReadOnlySpan' - // [await FromResult(Get02(x))]; - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await FromResult(Get02(x))").WithArguments("System.ReadOnlySpan").WithLocation(24, 13) + // (20,12): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // => MemoryMarshal.CreateReadOnlySpan( + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As, int>( + ref Unsafe.AsRef(in GetC(x).F[Get01()])), + 10)").WithArguments("System.ReadOnlySpan").WithLocation(20, 12) ); } @@ -14555,10 +14561,10 @@ static void M3(in C x) // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped in TBuffer)"" + IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped ref readonly TBuffer)"" IL_0006: call ""ref TElement System.Runtime.CompilerServices.Unsafe.As(ref TBuffer)"" IL_000b: ldarg.1 - IL_000c: call ""System.ReadOnlySpan System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan(scoped ref TElement, int)"" + IL_000c: call ""System.ReadOnlySpan System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan(scoped ref readonly TElement, int)"" IL_0011: ret } "); @@ -14652,7 +14658,7 @@ static void M3(in C x) // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped in TBuffer)"" + IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped ref readonly TBuffer)"" IL_0006: call ""ref TElement System.Runtime.CompilerServices.Unsafe.As(ref TBuffer)"" IL_000b: ldarg.1 IL_000c: call ""ref TElement System.Runtime.CompilerServices.Unsafe.Add(ref TElement, int)"" @@ -14743,7 +14749,7 @@ static void M3(in C x) // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 - IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped in TBuffer)"" + IL_0001: call ""ref TBuffer System.Runtime.CompilerServices.Unsafe.AsRef(scoped ref readonly TBuffer)"" IL_0006: call ""ref TElement System.Runtime.CompilerServices.Unsafe.As(ref TBuffer)"" IL_000b: ret } @@ -18487,7 +18493,7 @@ .locals init (Buffer4& V_0, IL_0019: dup IL_001a: ldind.i4 IL_001b: call ""void System.Console.Write(int)"" - IL_0020: call ""ref int System.Runtime.CompilerServices.Unsafe.AsRef(scoped in int)"" + IL_0020: call ""ref int System.Runtime.CompilerServices.Unsafe.AsRef(scoped ref readonly int)"" IL_0025: dup IL_0026: ldind.i4 IL_0027: ldc.i4.m1 @@ -18714,7 +18720,7 @@ .locals init (Buffer4& V_0, IL_0019: dup IL_001a: ldind.i4 IL_001b: call ""void System.Console.Write(int)"" - IL_0020: call ""ref int System.Runtime.CompilerServices.Unsafe.AsRef(scoped in int)"" + IL_0020: call ""ref int System.Runtime.CompilerServices.Unsafe.AsRef(scoped ref readonly int)"" IL_0025: dup IL_0026: ldind.i4 IL_0027: ldc.i4.m1 @@ -20154,30 +20160,45 @@ .locals init (int V_0, CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); } - [Fact] + [ConditionalFact(typeof(CoreClrOnly))] public void Foreach_InAsync_03() { var src = @" class Program { - static async void Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { + s_buffer[1] = 3; + foreach (ref int y in GetBuffer()) { + y *= y; + System.Console.Write(y); } await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static ref Buffer4 GetBuffer() => throw null; + static ref Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,26): error CS8177: Async methods cannot have by-reference locals +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // foreach (ref int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(6, 26) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedOutput = "09009"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -20598,30 +20619,48 @@ .locals init (int V_0, CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); } - [Fact] + [ConditionalFact(typeof(CoreClrOnly))] public void Foreach_InAsync_07() { var src = @" class Program { - static async void Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { + s_buffer[1] = 3; + + int i = 0; foreach (ref readonly int y in GetBuffer()) { + System.Console.Write(y); + s_buffer[i++]++; + System.Console.Write(y); + System.Console.Write(' '); } await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static ref readonly Buffer4 GetBuffer() => throw null; + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,35): error CS8177: Async methods cannot have by-reference locals +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // foreach (ref readonly int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(6, 35) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedOutput = "01 34 01 01 4"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -20798,132 +20837,209 @@ .locals init (int V_0, } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_01() + public void Foreach_InAsync_10() { var src = @" class Program { - static private Buffer4 F = default; - private static int index = 0; + static Buffer4 s_buffer; - static void Main() + static async System.Threading.Tasks.Task Main() { - foreach (var a in Test()) - {} + s_buffer[1] = 3; + + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static System.Collections.Generic.IEnumerable Test() + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26), + // (11,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 26)); + + var expectedOutput = "09009"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InAsync_11() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - yield return -1; + s_buffer[1] = 3; - foreach (var y in GetBuffer()) + foreach (ref int y in GetBuffer()) { - Increment(); - System.Console.Write(' '); + await System.Threading.Tasks.Task.Yield(); + y *= y; System.Console.Write(y); } - yield return -2; - } + await System.Threading.Tasks.Task.Yield(); - static ref Buffer4 GetBuffer() - { - System.Console.Write(-1); - return ref F; + System.Console.Write(s_buffer[1]); } - static void Increment() + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedDiagnostics = new[] + { + // (13,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(13, 13), + // (13,18): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(13, 18), + // (14,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(14, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_12() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - index++; + s_buffer[1] = 3; - if (index < 4) + foreach (ref int y in GetBuffer()) { - F[index] = index; + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } + + static ref Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; - verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", -@" + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedDiagnostics = new[] + { + // (10,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + }").WithArguments("Program.GetBuffer()").WithLocation(10, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_13() + { + var src = @" +class Program { - // Code size 126 (0x7e) - .maxstack 2 - .locals init (int V_0, - Buffer4& V_1, - int V_2) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__3.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: switch ( - IL_001b, - IL_0032, - IL_0075) - IL_0019: ldc.i4.0 - IL_001a: ret - IL_001b: ldarg.0 - IL_001c: ldc.i4.m1 - IL_001d: stfld ""int Program.d__3.<>1__state"" - IL_0022: ldarg.0 - IL_0023: ldc.i4.m1 - IL_0024: stfld ""int Program.d__3.<>2__current"" - IL_0029: ldarg.0 - IL_002a: ldc.i4.1 - IL_002b: stfld ""int Program.d__3.<>1__state"" - IL_0030: ldc.i4.1 - IL_0031: ret - IL_0032: ldarg.0 - IL_0033: ldc.i4.m1 - IL_0034: stfld ""int Program.d__3.<>1__state"" - IL_0039: call ""ref Buffer4 Program.GetBuffer()"" - IL_003e: stloc.1 - IL_003f: ldc.i4.0 - IL_0040: stloc.2 - IL_0041: br.s IL_0060 - IL_0043: ldloc.1 - IL_0044: ldloc.2 - IL_0045: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" - IL_004a: ldind.i4 - IL_004b: call ""void Program.Increment()"" - IL_0050: ldc.i4.s 32 - IL_0052: call ""void System.Console.Write(char)"" - IL_0057: call ""void System.Console.Write(int)"" - IL_005c: ldloc.2 - IL_005d: ldc.i4.1 - IL_005e: add - IL_005f: stloc.2 - IL_0060: ldloc.2 - IL_0061: ldc.i4.4 - IL_0062: blt.s IL_0043 - IL_0064: ldarg.0 - IL_0065: ldc.i4.s -2 - IL_0067: stfld ""int Program.d__3.<>2__current"" - IL_006c: ldarg.0 - IL_006d: ldc.i4.2 - IL_006e: stfld ""int Program.d__3.<>1__state"" - IL_0073: ldc.i4.1 - IL_0074: ret - IL_0075: ldarg.0 - IL_0076: ldc.i4.m1 - IL_0077: stfld ""int Program.d__3.<>1__state"" - IL_007c: ldc.i4.0 - IL_007d: ret + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() + { + s_buffer[1] = 3; + + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; } -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26), + // (11,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 26)); + + var expectedDiagnostics = new[] + { + // (11,31): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "buffer").WithLocation(11, 31) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } - [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_02() + [Fact] + public void Foreach_InAsync_14() { var src = @" +using System.Threading.Tasks; + class C { - public Buffer4 F = default; + public readonly Buffer4 F = default; } class Program @@ -20933,19 +21049,20 @@ class Program static void Main() { - foreach (var a in Test(c)) - {} + Test(c).Wait(); } - static System.Collections.Generic.IEnumerable Test(C x) + static async Task Test(C x) { - foreach (var y in x.F) + ref readonly Buffer4 f = ref x.F; + foreach (var y in f) { Increment(); System.Console.Write(' '); System.Console.Write(y); - yield return -1; + await Task.Yield(); + await Task.Delay(2); } } @@ -20955,146 +21072,266 @@ static void Increment() if (index < 4) { - c.F[index] = index; + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; } } } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; - verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", -@" + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (21,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(21, 35)); + + var expectedDiagnostics = new[] + { + // (22,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(22, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_15() + { + var src = @" +class Program { - // Code size 151 (0x97) - .maxstack 3 - .locals init (int V_0) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__3.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0070 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld ""int Program.d__3.<>1__state"" - IL_0017: ldarg.0 - IL_0018: ldarg.0 - IL_0019: ldfld ""C Program.d__3.x"" - IL_001e: stfld ""C Program.d__3.<>7__wrap2"" - IL_0023: ldarg.0 - IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" - IL_0029: ldfld ""Buffer4 C.F"" - IL_002e: pop - IL_002f: ldarg.0 - IL_0030: ldc.i4.0 - IL_0031: stfld ""int Program.d__3.<>7__wrap1"" - IL_0036: br.s IL_0085 - IL_0038: ldarg.0 - IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" - IL_003e: ldflda ""Buffer4 C.F"" - IL_0043: ldarg.0 - IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" - IL_0049: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" - IL_004e: ldind.i4 - IL_004f: call ""void Program.Increment()"" - IL_0054: ldc.i4.s 32 - IL_0056: call ""void System.Console.Write(char)"" - IL_005b: call ""void System.Console.Write(int)"" - IL_0060: ldarg.0 - IL_0061: ldc.i4.m1 - IL_0062: stfld ""int Program.d__3.<>2__current"" - IL_0067: ldarg.0 - IL_0068: ldc.i4.1 - IL_0069: stfld ""int Program.d__3.<>1__state"" - IL_006e: ldc.i4.1 - IL_006f: ret - IL_0070: ldarg.0 - IL_0071: ldc.i4.m1 - IL_0072: stfld ""int Program.d__3.<>1__state"" - IL_0077: ldarg.0 - IL_0078: ldarg.0 - IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" - IL_007e: ldc.i4.1 - IL_007f: add - IL_0080: stfld ""int Program.d__3.<>7__wrap1"" - IL_0085: ldarg.0 - IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" - IL_008b: ldc.i4.4 - IL_008c: blt.s IL_0038 - IL_008e: ldarg.0 - IL_008f: ldnull - IL_0090: stfld ""C Program.d__3.<>7__wrap2"" - IL_0095: ldc.i4.0 - IL_0096: ret + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() + { + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + } + + await System.Threading.Tasks.Task.Yield(); + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (8,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + }").WithArguments("Program.GetBuffer()").WithLocation(8, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] - public void Foreach_InIterator_03() + public void Foreach_InAsync_16() { var src = @" class Program { - static System.Collections.Generic.IEnumerable Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - foreach (ref int y in GetBuffer()) + foreach (ref readonly int y in GetBuffer()) { + await System.Threading.Tasks.Task.Yield(); + System.Console.Write(y); } - yield return -1; + await System.Threading.Tasks.Task.Yield(); } - static ref Buffer4 GetBuffer() => throw null; + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,26): error CS8176: Iterators cannot have by-reference locals - // foreach (ref int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(6, 26) - ); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (11,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(11, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InAsync_17() + { + var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + Test(c).Wait(); + } + + static async Task Test(C x) + { + foreach (ref readonly int y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + await Task.Yield(); + await Task.Delay(2); + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +" + Buffer4Definition; + var expectedOutput = " 0 1 2 3"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); } [Fact] - public void Foreach_InIterator_04() + public void Foreach_InAsync_18() { var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; +} + class Program { - static System.Collections.Generic.IEnumerable Test() + static async Task Test(C x) { - foreach (int y in GetBuffer()) + foreach (ref readonly int y in x.F) { - yield return -1; + await Task.Yield(); + System.Console.Write(y); } } +} +" + Buffer4Definition; - static ref Buffer4 GetBuffer() => throw null; + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (13,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in x.F) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 35)); + + var expectedDiagnostics = new[] + { + // (16,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(16, 34) + }; + + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_19() + { + var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyEmitDiagnostics( - // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. - // foreach (int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, - @"foreach (int y in GetBuffer()) +class Program +{ + static async Task Test(C x) + { + ref readonly Buffer4 f = ref x.F; + + foreach (var i in f) System.Console.Write(i); + + foreach (var y in f) { - yield return -1; - }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) - ); + System.Console.Write(y); + await Task.Yield(); + } + + foreach (var j in f) System.Console.Write(j); + + foreach (var z in f) + { + System.Console.Write(z); + await Task.Yield(); + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (13,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 35)); + + var expectedDiagnostics = new[] + { + // (17,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(17, 27), + // (23,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in f) System.Console.Write(j); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(23, 27), + // (25,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var z in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(25, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_05() + public void Foreach_InIterator_01() { var src = @" class Program @@ -21122,7 +21359,7 @@ static System.Collections.Generic.IEnumerable Test() yield return -2; } - static ref readonly Buffer4 GetBuffer() + static ref Buffer4 GetBuffer() { System.Console.Write(-1); return ref F; @@ -21174,14 +21411,14 @@ .locals init (int V_0, IL_0032: ldarg.0 IL_0033: ldc.i4.m1 IL_0034: stfld ""int Program.d__3.<>1__state"" - IL_0039: call ""ref readonly Buffer4 Program.GetBuffer()"" + IL_0039: call ""ref Buffer4 Program.GetBuffer()"" IL_003e: stloc.1 IL_003f: ldc.i4.0 IL_0040: stloc.2 IL_0041: br.s IL_0060 IL_0043: ldloc.1 IL_0044: ldloc.2 - IL_0045: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_0045: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" IL_004a: ldind.i4 IL_004b: call ""void Program.Increment()"" IL_0050: ldc.i4.s 32 @@ -21208,13 +21445,1009 @@ .locals init (int V_0, IL_007c: ldc.i4.0 IL_007d: ret } -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_02() + { + var src = @" +class C +{ + public Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (var y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + c.F[index] = index; + } + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 151 (0x97) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__3.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0070 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld ""int Program.d__3.<>1__state"" + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: ldfld ""C Program.d__3.x"" + IL_001e: stfld ""C Program.d__3.<>7__wrap2"" + IL_0023: ldarg.0 + IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" + IL_0029: ldfld ""Buffer4 C.F"" + IL_002e: pop + IL_002f: ldarg.0 + IL_0030: ldc.i4.0 + IL_0031: stfld ""int Program.d__3.<>7__wrap1"" + IL_0036: br.s IL_0085 + IL_0038: ldarg.0 + IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" + IL_003e: ldflda ""Buffer4 C.F"" + IL_0043: ldarg.0 + IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" + IL_0049: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" + IL_004e: ldind.i4 + IL_004f: call ""void Program.Increment()"" + IL_0054: ldc.i4.s 32 + IL_0056: call ""void System.Console.Write(char)"" + IL_005b: call ""void System.Console.Write(int)"" + IL_0060: ldarg.0 + IL_0061: ldc.i4.m1 + IL_0062: stfld ""int Program.d__3.<>2__current"" + IL_0067: ldarg.0 + IL_0068: ldc.i4.1 + IL_0069: stfld ""int Program.d__3.<>1__state"" + IL_006e: ldc.i4.1 + IL_006f: ret + IL_0070: ldarg.0 + IL_0071: ldc.i4.m1 + IL_0072: stfld ""int Program.d__3.<>1__state"" + IL_0077: ldarg.0 + IL_0078: ldarg.0 + IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" + IL_007e: ldc.i4.1 + IL_007f: add + IL_0080: stfld ""int Program.d__3.<>7__wrap1"" + IL_0085: ldarg.0 + IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" + IL_008b: ldc.i4.4 + IL_008c: blt.s IL_0038 + IL_008e: ldarg.0 + IL_008f: ldnull + IL_0090: stfld ""C Program.d__3.<>7__wrap2"" + IL_0095: ldc.i4.0 + IL_0096: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_03() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedOutput = "0090-19"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_04() + { + var src = @" +class Program +{ + static System.Collections.Generic.IEnumerable Test() + { + foreach (int y in GetBuffer()) + { + yield return -1; + } + } + + static ref Buffer4 GetBuffer() => throw null; +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + + comp.VerifyEmitDiagnostics( + // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, + @"foreach (int y in GetBuffer()) + { + yield return -1; + }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) + ); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_05() + { + var src = @" +class Program +{ + static private Buffer4 F = default; + private static int index = 0; + + static void Main() + { + foreach (var a in Test()) + {} + } + + static System.Collections.Generic.IEnumerable Test() + { + yield return -1; + + foreach (var y in GetBuffer()) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + } + + yield return -2; + } + + static ref readonly Buffer4 GetBuffer() + { + System.Console.Write(-1); + return ref F; + } + + static void Increment() + { + index++; + + if (index < 4) + { + F[index] = index; + } + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 126 (0x7e) + .maxstack 2 + .locals init (int V_0, + Buffer4& V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__3.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: switch ( + IL_001b, + IL_0032, + IL_0075) + IL_0019: ldc.i4.0 + IL_001a: ret + IL_001b: ldarg.0 + IL_001c: ldc.i4.m1 + IL_001d: stfld ""int Program.d__3.<>1__state"" + IL_0022: ldarg.0 + IL_0023: ldc.i4.m1 + IL_0024: stfld ""int Program.d__3.<>2__current"" + IL_0029: ldarg.0 + IL_002a: ldc.i4.1 + IL_002b: stfld ""int Program.d__3.<>1__state"" + IL_0030: ldc.i4.1 + IL_0031: ret + IL_0032: ldarg.0 + IL_0033: ldc.i4.m1 + IL_0034: stfld ""int Program.d__3.<>1__state"" + IL_0039: call ""ref readonly Buffer4 Program.GetBuffer()"" + IL_003e: stloc.1 + IL_003f: ldc.i4.0 + IL_0040: stloc.2 + IL_0041: br.s IL_0060 + IL_0043: ldloc.1 + IL_0044: ldloc.2 + IL_0045: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_004a: ldind.i4 + IL_004b: call ""void Program.Increment()"" + IL_0050: ldc.i4.s 32 + IL_0052: call ""void System.Console.Write(char)"" + IL_0057: call ""void System.Console.Write(int)"" + IL_005c: ldloc.2 + IL_005d: ldc.i4.1 + IL_005e: add + IL_005f: stloc.2 + IL_0060: ldloc.2 + IL_0061: ldc.i4.4 + IL_0062: blt.s IL_0043 + IL_0064: ldarg.0 + IL_0065: ldc.i4.s -2 + IL_0067: stfld ""int Program.d__3.<>2__current"" + IL_006c: ldarg.0 + IL_006d: ldc.i4.2 + IL_006e: stfld ""int Program.d__3.<>1__state"" + IL_0073: ldc.i4.1 + IL_0074: ret + IL_0075: ldarg.0 + IL_0076: ldc.i4.m1 + IL_0077: stfld ""int Program.d__3.<>1__state"" + IL_007c: ldc.i4.0 + IL_007d: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_06() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (var y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 151 (0x97) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__3.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0070 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld ""int Program.d__3.<>1__state"" + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: ldfld ""C Program.d__3.x"" + IL_001e: stfld ""C Program.d__3.<>7__wrap2"" + IL_0023: ldarg.0 + IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" + IL_0029: ldfld ""Buffer4 C.F"" + IL_002e: pop + IL_002f: ldarg.0 + IL_0030: ldc.i4.0 + IL_0031: stfld ""int Program.d__3.<>7__wrap1"" + IL_0036: br.s IL_0085 + IL_0038: ldarg.0 + IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" + IL_003e: ldflda ""Buffer4 C.F"" + IL_0043: ldarg.0 + IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" + IL_0049: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_004e: ldind.i4 + IL_004f: call ""void Program.Increment()"" + IL_0054: ldc.i4.s 32 + IL_0056: call ""void System.Console.Write(char)"" + IL_005b: call ""void System.Console.Write(int)"" + IL_0060: ldarg.0 + IL_0061: ldc.i4.m1 + IL_0062: stfld ""int Program.d__3.<>2__current"" + IL_0067: ldarg.0 + IL_0068: ldc.i4.1 + IL_0069: stfld ""int Program.d__3.<>1__state"" + IL_006e: ldc.i4.1 + IL_006f: ret + IL_0070: ldarg.0 + IL_0071: ldc.i4.m1 + IL_0072: stfld ""int Program.d__3.<>1__state"" + IL_0077: ldarg.0 + IL_0078: ldarg.0 + IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" + IL_007e: ldc.i4.1 + IL_007f: add + IL_0080: stfld ""int Program.d__3.<>7__wrap1"" + IL_0085: ldarg.0 + IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" + IL_008b: ldc.i4.4 + IL_008c: blt.s IL_0038 + IL_008e: ldarg.0 + IL_008f: ldnull + IL_0090: stfld ""C Program.d__3.<>7__wrap2"" + IL_0095: ldc.i4.0 + IL_0096: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_07() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + int i = 0; + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + s_buffer[i++]++; + System.Console.Write(y); + System.Console.Write(' '); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (19,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 35)); + + var expectedOutput = "01 01 34 01 -14"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_08() + { + var src = @" +class Program +{ + static System.Collections.Generic.IEnumerable Test() + { + foreach (int y in GetBuffer()) + { + yield return -1; + } + } + + static ref readonly Buffer4 GetBuffer() => throw null; +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + + comp.VerifyEmitDiagnostics( + // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, + @"foreach (int y in GetBuffer()) + { + yield return -1; + }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) + ); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_09() + { + var src = @" +class Program +{ + static void Main() + { + foreach (var a in Test()) + {} + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (var y in GetBuffer()) + { + System.Console.Write(' '); + System.Console.Write(y); + yield return -1; + } + } + + static Buffer4 GetBuffer() + { + Buffer4 x = default; + x[0] = 111; + x[1] = 112; + x[2] = 113; + x[3] = 114; + + System.Console.Write(-1); + return x; + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__1.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 121 (0x79) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__1.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0059 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld ""int Program.d__1.<>1__state"" + IL_0017: ldarg.0 + IL_0018: call ""Buffer4 Program.GetBuffer()"" + IL_001d: stfld ""Buffer4 Program.d__1.<>7__wrap1"" + IL_0022: ldarg.0 + IL_0023: ldc.i4.0 + IL_0024: stfld ""int Program.d__1.<>7__wrap2"" + IL_0029: br.s IL_006e + IL_002b: ldarg.0 + IL_002c: ldflda ""Buffer4 Program.d__1.<>7__wrap1"" + IL_0031: ldarg.0 + IL_0032: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0037: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_003c: ldind.i4 + IL_003d: ldc.i4.s 32 + IL_003f: call ""void System.Console.Write(char)"" + IL_0044: call ""void System.Console.Write(int)"" + IL_0049: ldarg.0 + IL_004a: ldc.i4.m1 + IL_004b: stfld ""int Program.d__1.<>2__current"" + IL_0050: ldarg.0 + IL_0051: ldc.i4.1 + IL_0052: stfld ""int Program.d__1.<>1__state"" + IL_0057: ldc.i4.1 + IL_0058: ret + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: stfld ""int Program.d__1.<>1__state"" + IL_0060: ldarg.0 + IL_0061: ldarg.0 + IL_0062: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0067: ldc.i4.1 + IL_0068: add + IL_0069: stfld ""int Program.d__1.<>7__wrap2"" + IL_006e: ldarg.0 + IL_006f: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0074: ldc.i4.4 + IL_0075: blt.s IL_002b + IL_0077: ldc.i4.0 + IL_0078: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_10() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26), + // (19,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 26)); + + var expectedOutput = "0090-19"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_11() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref int y in GetBuffer()) + { + yield return 1; + y *= y; + System.Console.Write(y); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedDiagnostics = new[] + { + // (21,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(21, 13), + // (21,18): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(21, 18), + // (22,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(22, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_12() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + yield return 1; + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedDiagnostics = new[] + { + // (18,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + yield return 1; + }").WithArguments("Program.GetBuffer()").WithLocation(18, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_13() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + yield return 1; + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26), + // (19,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 26)); + + var expectedDiagnostics = new[] + { + // (19,31): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "buffer").WithLocation(19, 31) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_14() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + ref readonly Buffer4 f = ref x.F; + foreach (var y in f) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (20,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(20, 35)); + + var expectedDiagnostics = new[] + { + // (21,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(21, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_15() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + yield return 1; + } + + yield return -1; + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (8,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + yield return 1; + }").WithArguments("Program.GetBuffer()").WithLocation(8, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_16() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref readonly int y in GetBuffer()) + { + yield return 1; + System.Console.Write(y); + } + + yield return -1; + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (11,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(11, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_06() + public void Foreach_InIterator_17() { var src = @" class C @@ -21235,7 +22468,7 @@ static void Main() static System.Collections.Generic.IEnumerable Test(C x) { - foreach (var y in x.F) + foreach (ref readonly int y in x.F) { Increment(); System.Console.Write(' '); @@ -21255,240 +22488,107 @@ static void Increment() } } } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); - - verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", -@" -{ - // Code size 151 (0x97) - .maxstack 3 - .locals init (int V_0) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__3.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0070 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld ""int Program.d__3.<>1__state"" - IL_0017: ldarg.0 - IL_0018: ldarg.0 - IL_0019: ldfld ""C Program.d__3.x"" - IL_001e: stfld ""C Program.d__3.<>7__wrap2"" - IL_0023: ldarg.0 - IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" - IL_0029: ldfld ""Buffer4 C.F"" - IL_002e: pop - IL_002f: ldarg.0 - IL_0030: ldc.i4.0 - IL_0031: stfld ""int Program.d__3.<>7__wrap1"" - IL_0036: br.s IL_0085 - IL_0038: ldarg.0 - IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" - IL_003e: ldflda ""Buffer4 C.F"" - IL_0043: ldarg.0 - IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" - IL_0049: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" - IL_004e: ldind.i4 - IL_004f: call ""void Program.Increment()"" - IL_0054: ldc.i4.s 32 - IL_0056: call ""void System.Console.Write(char)"" - IL_005b: call ""void System.Console.Write(int)"" - IL_0060: ldarg.0 - IL_0061: ldc.i4.m1 - IL_0062: stfld ""int Program.d__3.<>2__current"" - IL_0067: ldarg.0 - IL_0068: ldc.i4.1 - IL_0069: stfld ""int Program.d__3.<>1__state"" - IL_006e: ldc.i4.1 - IL_006f: ret - IL_0070: ldarg.0 - IL_0071: ldc.i4.m1 - IL_0072: stfld ""int Program.d__3.<>1__state"" - IL_0077: ldarg.0 - IL_0078: ldarg.0 - IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" - IL_007e: ldc.i4.1 - IL_007f: add - IL_0080: stfld ""int Program.d__3.<>7__wrap1"" - IL_0085: ldarg.0 - IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" - IL_008b: ldc.i4.4 - IL_008c: blt.s IL_0038 - IL_008e: ldarg.0 - IL_008f: ldnull - IL_0090: stfld ""C Program.d__3.<>7__wrap2"" - IL_0095: ldc.i4.0 - IL_0096: ret -} -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; + var expectedOutput = " 0 1 2 3"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); } [Fact] - public void Foreach_InIterator_07() + public void Foreach_InIterator_18() { var src = @" -class Program +class C { - static System.Collections.Generic.IEnumerable Test() - { - foreach (ref readonly int y in GetBuffer()) - { - } - - yield return -1; - } - - static ref readonly Buffer4 GetBuffer() => throw null; + public readonly Buffer4 F = default; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,35): error CS8176: Iterators cannot have by-reference locals - // foreach (ref readonly int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(6, 35) - ); - } - [Fact] - public void Foreach_InIterator_08() - { - var src = @" class Program { - static System.Collections.Generic.IEnumerable Test() + static System.Collections.Generic.IEnumerable Test(C x) { - foreach (int y in GetBuffer()) + foreach (ref readonly int y in x.F) { yield return -1; + System.Console.Write(y); } } - - static ref readonly Buffer4 GetBuffer() => throw null; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); +" + Buffer4Definition; - comp.VerifyEmitDiagnostics( - // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. - // foreach (int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, - @"foreach (int y in GetBuffer()) - { - yield return -1; - }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) - ); + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in x.F) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedDiagnostics = new[] + { + // (14,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(14, 34) + }; + + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } - [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_09() + [Fact] + public void Foreach_InIterator_19() { var src = @" +class C +{ + public readonly Buffer4 F = default; +} + class Program { - static void Main() + static System.Collections.Generic.IEnumerable Test(C x) { - foreach (var a in Test()) - {} - } + ref readonly Buffer4 f = ref x.F; - static System.Collections.Generic.IEnumerable Test() - { - foreach (var y in GetBuffer()) + foreach (var i in f) System.Console.Write(i); + + foreach (var y in f) { - System.Console.Write(' '); System.Console.Write(y); yield return -1; } - } - static Buffer4 GetBuffer() - { - Buffer4 x = default; - x[0] = 111; - x[1] = 112; - x[2] = 113; - x[3] = 114; - - System.Console.Write(-1); - return x; + foreach (var j in f) System.Console.Write(j); + + foreach (var z in f) + { + System.Console.Write(z); + yield return -2; + } } } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); +" + Buffer4Definition; - verifier.VerifyIL("Program.d__1.System.Collections.IEnumerator.MoveNext", -@" -{ - // Code size 121 (0x79) - .maxstack 3 - .locals init (int V_0) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__1.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0059 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld ""int Program.d__1.<>1__state"" - IL_0017: ldarg.0 - IL_0018: call ""Buffer4 Program.GetBuffer()"" - IL_001d: stfld ""Buffer4 Program.d__1.<>7__wrap1"" - IL_0022: ldarg.0 - IL_0023: ldc.i4.0 - IL_0024: stfld ""int Program.d__1.<>7__wrap2"" - IL_0029: br.s IL_006e - IL_002b: ldarg.0 - IL_002c: ldflda ""Buffer4 Program.d__1.<>7__wrap1"" - IL_0031: ldarg.0 - IL_0032: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0037: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" - IL_003c: ldind.i4 - IL_003d: ldc.i4.s 32 - IL_003f: call ""void System.Console.Write(char)"" - IL_0044: call ""void System.Console.Write(int)"" - IL_0049: ldarg.0 - IL_004a: ldc.i4.m1 - IL_004b: stfld ""int Program.d__1.<>2__current"" - IL_0050: ldarg.0 - IL_0051: ldc.i4.1 - IL_0052: stfld ""int Program.d__1.<>1__state"" - IL_0057: ldc.i4.1 - IL_0058: ret - IL_0059: ldarg.0 - IL_005a: ldc.i4.m1 - IL_005b: stfld ""int Program.d__1.<>1__state"" - IL_0060: ldarg.0 - IL_0061: ldarg.0 - IL_0062: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0067: ldc.i4.1 - IL_0068: add - IL_0069: stfld ""int Program.d__1.<>7__wrap2"" - IL_006e: ldarg.0 - IL_006f: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0074: ldc.i4.4 - IL_0075: blt.s IL_002b - IL_0077: ldc.i4.0 - IL_0078: ret -} -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedDiagnostics = new[] + { + // (15,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(15, 27), + // (21,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in f) System.Console.Write(j); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(21, 27), + // (23,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var z in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(23, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [ConditionalFact(typeof(CoreClrOnly))] @@ -22006,5 +23106,218 @@ struct ThreeStringBuffer { var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); } + + [Fact] + public void Initialization_Await_RefStruct() + { + var src = """ + using System.Threading.Tasks; + + var b = new Buffer(); + b[0] = await GetInt(); + b[1] = await GetInt(); + + static Task GetInt() => Task.FromResult(42); + + [System.Runtime.CompilerServices.InlineArray(4)] + ref struct Buffer + { + private int _element0; + } + """; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (3,1): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var b = new Buffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 1), + // (4,1): error CS0306: The type 'Buffer' may not be used as a type argument + // b[0] = await GetInt(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "b[0]").WithArguments("Buffer").WithLocation(4, 1), + // (5,1): error CS0306: The type 'Buffer' may not be used as a type argument + // b[1] = await GetInt(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "b[1]").WithArguments("Buffer").WithLocation(5, 1), + // (10,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument. + // ref struct Buffer + Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "Buffer").WithLocation(10, 12)); + + var expectedDiagnostics = new[] + { + // (4,1): error CS0306: The type 'Buffer' may not be used as a type argument + // b[0] = await GetInt(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "b[0]").WithArguments("Buffer").WithLocation(4, 1), + // (5,1): error CS0306: The type 'Buffer' may not be used as a type argument + // b[1] = await GetInt(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "b[1]").WithArguments("Buffer").WithLocation(5, 1), + // (10,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument. + // ref struct Buffer + Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "Buffer").WithLocation(10, 12) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Initialization_Await() + { + var src = """ + using System.Threading.Tasks; + + var b = new Buffer(); + b[0] = await GetInt(); + System.Console.Write(b[1]); + b[1] = await GetInt(); + System.Console.Write(b[1]); + + static Task GetInt() => Task.FromResult(42); + + [System.Runtime.CompilerServices.InlineArray(4)] + struct Buffer + { + private int _element0; + } + """; + foreach (var parseOptions in new[] { TestOptions.Regular12, TestOptions.RegularNext, TestOptions.RegularPreview }) + { + var verifier = CompileAndVerify(src, expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "042", + parseOptions: parseOptions, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ + { + // Code size 316 (0x13c) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<
$>d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0054 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00cb + IL_0011: ldarg.0 + IL_0012: ldflda "Buffer Program.<
$>d__0.5__2" + IL_0017: initobj "Buffer" + IL_001d: call "System.Threading.Tasks.Task Program.<
$>g__GetInt|0_0()" + IL_0022: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0027: stloc.2 + IL_0028: ldloca.s V_2 + IL_002a: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0070 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int Program.<
$>d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.2 + IL_003c: stfld "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_0041: ldarg.0 + IL_0042: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_0047: ldloca.s V_2 + IL_0049: ldarg.0 + IL_004a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.<
$>d__0)" + IL_004f: leave IL_013b + IL_0054: ldarg.0 + IL_0055: ldfld "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_005a: stloc.2 + IL_005b: ldarg.0 + IL_005c: ldflda "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_0061: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0067: ldarg.0 + IL_0068: ldc.i4.m1 + IL_0069: dup + IL_006a: stloc.0 + IL_006b: stfld "int Program.<
$>d__0.<>1__state" + IL_0070: ldloca.s V_2 + IL_0072: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0077: stloc.1 + IL_0078: ldarg.0 + IL_0079: ldflda "Buffer Program.<
$>d__0.5__2" + IL_007e: call "ref int .InlineArrayFirstElementRef(ref Buffer)" + IL_0083: ldloc.1 + IL_0084: stind.i4 + IL_0085: ldarg.0 + IL_0086: ldflda "Buffer Program.<
$>d__0.5__2" + IL_008b: ldc.i4.1 + IL_008c: call "ref int .InlineArrayElementRef(ref Buffer, int)" + IL_0091: ldind.i4 + IL_0092: call "void System.Console.Write(int)" + IL_0097: call "System.Threading.Tasks.Task Program.<
$>g__GetInt|0_0()" + IL_009c: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00a1: stloc.2 + IL_00a2: ldloca.s V_2 + IL_00a4: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00a9: brtrue.s IL_00e7 + IL_00ab: ldarg.0 + IL_00ac: ldc.i4.1 + IL_00ad: dup + IL_00ae: stloc.0 + IL_00af: stfld "int Program.<
$>d__0.<>1__state" + IL_00b4: ldarg.0 + IL_00b5: ldloc.2 + IL_00b6: stfld "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_00bb: ldarg.0 + IL_00bc: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_00c1: ldloca.s V_2 + IL_00c3: ldarg.0 + IL_00c4: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.<
$>d__0)" + IL_00c9: leave.s IL_013b + IL_00cb: ldarg.0 + IL_00cc: ldfld "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_00d1: stloc.2 + IL_00d2: ldarg.0 + IL_00d3: ldflda "System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1" + IL_00d8: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00de: ldarg.0 + IL_00df: ldc.i4.m1 + IL_00e0: dup + IL_00e1: stloc.0 + IL_00e2: stfld "int Program.<
$>d__0.<>1__state" + IL_00e7: ldloca.s V_2 + IL_00e9: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00ee: stloc.1 + IL_00ef: ldarg.0 + IL_00f0: ldflda "Buffer Program.<
$>d__0.5__2" + IL_00f5: ldc.i4.1 + IL_00f6: call "ref int .InlineArrayElementRef(ref Buffer, int)" + IL_00fb: ldloc.1 + IL_00fc: stind.i4 + IL_00fd: ldarg.0 + IL_00fe: ldflda "Buffer Program.<
$>d__0.5__2" + IL_0103: ldc.i4.1 + IL_0104: call "ref int .InlineArrayElementRef(ref Buffer, int)" + IL_0109: ldind.i4 + IL_010a: call "void System.Console.Write(int)" + IL_010f: leave.s IL_0128 + } + catch System.Exception + { + IL_0111: stloc.3 + IL_0112: ldarg.0 + IL_0113: ldc.i4.s -2 + IL_0115: stfld "int Program.<
$>d__0.<>1__state" + IL_011a: ldarg.0 + IL_011b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_0120: ldloc.3 + IL_0121: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0126: leave.s IL_013b + } + IL_0128: ldarg.0 + IL_0129: ldc.i4.s -2 + IL_012b: stfld "int Program.<
$>d__0.<>1__state" + IL_0130: ldarg.0 + IL_0131: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_0136: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_013b: ret + } + """); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs index 61b08bdf399e5..61614ef54b64e 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs @@ -1861,9 +1861,6 @@ public void Await() } """; CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (4,7): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(4, 7), // (6,5): error CS1996: Cannot await in the body of a lock statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInLock, "await Task.Yield()").WithLocation(6, 5)); @@ -1875,19 +1872,137 @@ public void AsyncMethod() var source = """ #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; + using System.Threading.Tasks; class C { - async void M() + static async Task Main() { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } } } """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (8,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(8, 15)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int C.
d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int C.
d__0.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int C.
d__0.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int C.
d__0.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1899,17 +2014,527 @@ public void AsyncMethod_WithAwait() class C { - async void M() + static async Task Main() { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); } } """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (9,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(9, 15)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + C.
d__0 V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int C.
d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int C.
d__0.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int C.
d__0.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int C.
d__0.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int C.
d__0.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int C.
d__0.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int C.
d__0.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int C.
d__0.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int C.
d__0.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int C.
d__0.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int C.
d__0.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int C.
d__0.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); + } + + [Fact] + public void AsyncMethod_AwaitResource() + { + var source = """ + #pragma warning disable 1998 // async method lacks 'await' operators + using System.Threading; + using System.Threading.Tasks; + + class C + { + static async Task Main() + { + lock (await GetLock()) { System.Console.Write("L"); } + } + + static async Task GetLock() => new Lock(); + } + """; + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 211 (0xd3) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.
d__0 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_000c + IL_000a: br.s IL_000e + IL_000c: br.s IL_004a + IL_000e: nop + IL_000f: call "System.Threading.Tasks.Task C.GetLock()" + IL_0014: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0019: stloc.2 + IL_001a: ldloca.s V_2 + IL_001c: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0021: brtrue.s IL_0066 + IL_0023: ldarg.0 + IL_0024: ldc.i4.0 + IL_0025: dup + IL_0026: stloc.0 + IL_0027: stfld "int C.
d__0.<>1__state" + IL_002c: ldarg.0 + IL_002d: ldloc.2 + IL_002e: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0033: ldarg.0 + IL_0034: stloc.3 + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003b: ldloca.s V_2 + IL_003d: ldloca.s V_3 + IL_003f: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__0)" + IL_0044: nop + IL_0045: leave IL_00d2 + IL_004a: ldarg.0 + IL_004b: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0050: stloc.2 + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0057: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_005d: ldarg.0 + IL_005e: ldc.i4.m1 + IL_005f: dup + IL_0060: stloc.0 + IL_0061: stfld "int C.
d__0.<>1__state" + IL_0066: ldarg.0 + IL_0067: ldloca.s V_2 + IL_0069: call "System.Threading.Lock System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_006e: stfld "System.Threading.Lock C.
d__0.<>s__1" + IL_0073: ldarg.0 + IL_0074: ldfld "System.Threading.Lock C.
d__0.<>s__1" + IL_0079: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_007e: stloc.1 + IL_007f: ldarg.0 + IL_0080: ldnull + IL_0081: stfld "System.Threading.Lock C.
d__0.<>s__1" + .try + { + IL_0086: nop + IL_0087: ldstr "L" + IL_008c: call "void System.Console.Write(string)" + IL_0091: nop + IL_0092: nop + IL_0093: leave.s IL_00a2 + } + finally + { + IL_0095: ldloc.0 + IL_0096: ldc.i4.0 + IL_0097: bge.s IL_00a1 + IL_0099: ldloca.s V_1 + IL_009b: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a0: nop + IL_00a1: endfinally + } + IL_00a2: leave.s IL_00be + } + catch System.Exception + { + IL_00a4: stloc.s V_4 + IL_00a6: ldarg.0 + IL_00a7: ldc.i4.s -2 + IL_00a9: stfld "int C.
d__0.<>1__state" + IL_00ae: ldarg.0 + IL_00af: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00b4: ldloc.s V_4 + IL_00b6: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_00bb: nop + IL_00bc: leave.s IL_00d2 + } + IL_00be: ldarg.0 + IL_00bf: ldc.i4.s -2 + IL_00c1: stfld "int C.
d__0.<>1__state" + IL_00c6: ldarg.0 + IL_00c7: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00cc: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00d1: nop + IL_00d2: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 172 (0xac) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_003e + IL_000a: call "System.Threading.Tasks.Task C.GetLock()" + IL_000f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0014: stloc.2 + IL_0015: ldloca.s V_2 + IL_0017: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_001c: brtrue.s IL_005a + IL_001e: ldarg.0 + IL_001f: ldc.i4.0 + IL_0020: dup + IL_0021: stloc.0 + IL_0022: stfld "int C.
d__0.<>1__state" + IL_0027: ldarg.0 + IL_0028: ldloc.2 + IL_0029: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_002e: ldarg.0 + IL_002f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0034: ldloca.s V_2 + IL_0036: ldarg.0 + IL_0037: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__0)" + IL_003c: leave.s IL_00ab + IL_003e: ldarg.0 + IL_003f: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0044: stloc.2 + IL_0045: ldarg.0 + IL_0046: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_004b: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0051: ldarg.0 + IL_0052: ldc.i4.m1 + IL_0053: dup + IL_0054: stloc.0 + IL_0055: stfld "int C.
d__0.<>1__state" + IL_005a: ldloca.s V_2 + IL_005c: call "System.Threading.Lock System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0061: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0066: stloc.1 + .try + { + IL_0067: ldstr "L" + IL_006c: call "void System.Console.Write(string)" + IL_0071: leave.s IL_007f + } + finally + { + IL_0073: ldloc.0 + IL_0074: ldc.i4.0 + IL_0075: bge.s IL_007e + IL_0077: ldloca.s V_1 + IL_0079: call "void System.Threading.Lock.Scope.Dispose()" + IL_007e: endfinally + } + IL_007f: leave.s IL_0098 + } + catch System.Exception + { + IL_0081: stloc.3 + IL_0082: ldarg.0 + IL_0083: ldc.i4.s -2 + IL_0085: stfld "int C.
d__0.<>1__state" + IL_008a: ldarg.0 + IL_008b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0090: ldloc.3 + IL_0091: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0096: leave.s IL_00ab + } + IL_0098: ldarg.0 + IL_0099: ldc.i4.s -2 + IL_009b: stfld "int C.
d__0.<>1__state" + IL_00a0: ldarg.0 + IL_00a1: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00a6: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00ab: ret + } + """); } [Fact] @@ -1921,15 +2546,132 @@ public void AsyncLocalFunction() async void local() { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } } local(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (6,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(6, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1939,18 +2681,304 @@ public void AsyncLocalFunction_WithAwait() using System.Threading; using System.Threading.Tasks; - async void local() + async Task local() { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); } - local(); + await local(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (7,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(7, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + Program.<<
$>g__local|0_0>d V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); } [Fact] @@ -1962,13 +2990,132 @@ public void AsyncLambda() var lam = async () => { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } }; + + await lam(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (6,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(6, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1981,13 +3128,301 @@ public void AsyncLambda_WithAwait() var lam = async () => { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); }; + + await lam(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (7,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(7, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + Program.<>c.<<
$>b__0_0>d V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); } [Fact] @@ -2011,9 +3446,93 @@ IEnumerable M() } """; CreateCompilation([source, LockTypeDefinition]).VerifyEmitDiagnostics( - // (9,15): error CS4013: Instance of type 'Lock.Scope' cannot be used inside a nested function, query expression, iterator block or async method + // (9,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15), + // (11,13): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(11, 13)); + } + + [Fact] + public void Yield_Break() + { + var source = """ + #pragma warning disable CS0162 // Unreachable code detected + using System; + using System.Collections.Generic; + using System.Threading; + + static class Program + { + static void Main() + { + foreach (var x in M()) + { + Console.Write(x); + } + } + + static IEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + yield break; + Console.Write("B"); + } + yield return 2; + } + } + """; + var expectedOutput = "1ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Yield_AroundOnly() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + + static class Program + { + static void Main() + { + foreach (var x in M()) + { + Console.Write(x); + } + } + + static IEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + } + yield return 2; + } + } + """; + var expectedOutput = "1ELD2"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); } [Fact] @@ -2038,10 +3557,455 @@ async IAsyncEnumerable M() } } """; - CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyDiagnostics( - // (10,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyEmitDiagnostics( + // (10,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(10, 15)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(10, 15), + // (12,13): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(12, 13)); + } + + [Fact] + public void Yield_Async_Break() + { + var source = """ + #pragma warning disable CS0162 // Unreachable code detected + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + static class Program + { + static async Task Main() + { + await foreach (var x in M()) + { + Console.Write(x); + } + } + + async static IAsyncEnumerable M() + { + yield return 1; + await Task.Yield(); + lock (new Lock()) + { + Console.Write("L"); + yield break; + Console.Write("B"); + } + await Task.Yield(); + yield return 2; + } + } + """; + var expectedOutput = "1ELD"; + var comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.DebugExe); + verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Yield_Async_AroundOnly() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + static class Program + { + static async Task Main() + { + await foreach (var x in M()) + { + Console.Write(x); + } + } + + static async IAsyncEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + } + await Task.Yield(); + yield return 2; + } + } + """; + var expectedOutput = "1ELD2"; + var comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 414 (0x19e) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + Program.d__1 V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_002a, + IL_002f, + IL_0031, + IL_0038, + IL_0038, + IL_0033) + IL_0028: br.s IL_0038 + IL_002a: br IL_0125 + IL_002f: br.s IL_0065 + IL_0031: br.s IL_0038 + IL_0033: br IL_00ee + IL_0038: ldarg.0 + IL_0039: ldfld "bool Program.d__1.<>w__disposeMode" + IL_003e: brfalse.s IL_0045 + IL_0040: leave IL_0167 + IL_0045: ldarg.0 + IL_0046: ldc.i4.m1 + IL_0047: dup + IL_0048: stloc.0 + IL_0049: stfld "int Program.d__1.<>1__state" + IL_004e: nop + IL_004f: ldarg.0 + IL_0050: ldc.i4.1 + IL_0051: stfld "int Program.d__1.<>2__current" + IL_0056: ldarg.0 + IL_0057: ldc.i4.s -4 + IL_0059: dup + IL_005a: stloc.0 + IL_005b: stfld "int Program.d__1.<>1__state" + IL_0060: leave IL_0190 + IL_0065: ldarg.0 + IL_0066: ldc.i4.m1 + IL_0067: dup + IL_0068: stloc.0 + IL_0069: stfld "int Program.d__1.<>1__state" + IL_006e: ldarg.0 + IL_006f: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0074: brfalse.s IL_007b + IL_0076: leave IL_0167 + IL_007b: newobj "System.Threading.Lock..ctor()" + IL_0080: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0085: stloc.1 + .try + { + IL_0086: nop + IL_0087: ldstr "L" + IL_008c: call "void System.Console.Write(string)" + IL_0091: nop + IL_0092: nop + IL_0093: leave.s IL_00a2 + } + finally + { + IL_0095: ldloc.0 + IL_0096: ldc.i4.m1 + IL_0097: bne.un.s IL_00a1 + IL_0099: ldloca.s V_1 + IL_009b: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a0: nop + IL_00a1: endfinally + } + IL_00a2: ldarg.0 + IL_00a3: ldfld "bool Program.d__1.<>w__disposeMode" + IL_00a8: brfalse.s IL_00af + IL_00aa: leave IL_0167 + IL_00af: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00b4: stloc.3 + IL_00b5: ldloca.s V_3 + IL_00b7: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00bc: stloc.2 + IL_00bd: ldloca.s V_2 + IL_00bf: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00c4: brtrue.s IL_010a + IL_00c6: ldarg.0 + IL_00c7: ldc.i4.0 + IL_00c8: dup + IL_00c9: stloc.0 + IL_00ca: stfld "int Program.d__1.<>1__state" + IL_00cf: ldarg.0 + IL_00d0: ldloc.2 + IL_00d1: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00d6: ldarg.0 + IL_00d7: stloc.s V_4 + IL_00d9: ldarg.0 + IL_00da: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00df: ldloca.s V_2 + IL_00e1: ldloca.s V_4 + IL_00e3: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.d__1)" + IL_00e8: nop + IL_00e9: leave IL_019d + IL_00ee: ldarg.0 + IL_00ef: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00f4: stloc.2 + IL_00f5: ldarg.0 + IL_00f6: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00fb: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_0101: ldarg.0 + IL_0102: ldc.i4.m1 + IL_0103: dup + IL_0104: stloc.0 + IL_0105: stfld "int Program.d__1.<>1__state" + IL_010a: ldloca.s V_2 + IL_010c: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0111: nop + IL_0112: ldarg.0 + IL_0113: ldc.i4.2 + IL_0114: stfld "int Program.d__1.<>2__current" + IL_0119: ldarg.0 + IL_011a: ldc.i4.s -5 + IL_011c: dup + IL_011d: stloc.0 + IL_011e: stfld "int Program.d__1.<>1__state" + IL_0123: leave.s IL_0190 + IL_0125: ldarg.0 + IL_0126: ldc.i4.m1 + IL_0127: dup + IL_0128: stloc.0 + IL_0129: stfld "int Program.d__1.<>1__state" + IL_012e: ldarg.0 + IL_012f: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0134: brfalse.s IL_0138 + IL_0136: leave.s IL_0167 + IL_0138: leave.s IL_0167 + } + catch System.Exception + { + IL_013a: stloc.s V_5 + IL_013c: ldarg.0 + IL_013d: ldc.i4.s -2 + IL_013f: stfld "int Program.d__1.<>1__state" + IL_0144: ldarg.0 + IL_0145: ldc.i4.0 + IL_0146: stfld "int Program.d__1.<>2__current" + IL_014b: ldarg.0 + IL_014c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0151: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0156: nop + IL_0157: ldarg.0 + IL_0158: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_015d: ldloc.s V_5 + IL_015f: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0164: nop + IL_0165: leave.s IL_019d + } + IL_0167: ldarg.0 + IL_0168: ldc.i4.s -2 + IL_016a: stfld "int Program.d__1.<>1__state" + IL_016f: ldarg.0 + IL_0170: ldc.i4.0 + IL_0171: stfld "int Program.d__1.<>2__current" + IL_0176: ldarg.0 + IL_0177: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_017c: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0181: nop + IL_0182: ldarg.0 + IL_0183: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0188: ldc.i4.0 + IL_0189: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_018e: nop + IL_018f: ret + IL_0190: ldarg.0 + IL_0191: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0196: ldc.i4.1 + IL_0197: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_019c: nop + IL_019d: ret + } + """); + + comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.ReleaseExe); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 383 (0x17f) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + Program.d__1 V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_010e, + IL_0054, + IL_0028, + IL_0028, + IL_0028, + IL_00d8) + IL_0028: ldarg.0 + IL_0029: ldfld "bool Program.d__1.<>w__disposeMode" + IL_002e: brfalse.s IL_0035 + IL_0030: leave IL_014b + IL_0035: ldarg.0 + IL_0036: ldc.i4.m1 + IL_0037: dup + IL_0038: stloc.0 + IL_0039: stfld "int Program.d__1.<>1__state" + IL_003e: ldarg.0 + IL_003f: ldc.i4.1 + IL_0040: stfld "int Program.d__1.<>2__current" + IL_0045: ldarg.0 + IL_0046: ldc.i4.s -4 + IL_0048: dup + IL_0049: stloc.0 + IL_004a: stfld "int Program.d__1.<>1__state" + IL_004f: leave IL_0172 + IL_0054: ldarg.0 + IL_0055: ldc.i4.m1 + IL_0056: dup + IL_0057: stloc.0 + IL_0058: stfld "int Program.d__1.<>1__state" + IL_005d: ldarg.0 + IL_005e: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0063: brfalse.s IL_006a + IL_0065: leave IL_014b + IL_006a: newobj "System.Threading.Lock..ctor()" + IL_006f: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0074: stloc.1 + .try + { + IL_0075: ldstr "L" + IL_007a: call "void System.Console.Write(string)" + IL_007f: leave.s IL_008d + } + finally + { + IL_0081: ldloc.0 + IL_0082: ldc.i4.m1 + IL_0083: bne.un.s IL_008c + IL_0085: ldloca.s V_1 + IL_0087: call "void System.Threading.Lock.Scope.Dispose()" + IL_008c: endfinally + } + IL_008d: ldarg.0 + IL_008e: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0093: brfalse.s IL_009a + IL_0095: leave IL_014b + IL_009a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_009f: stloc.3 + IL_00a0: ldloca.s V_3 + IL_00a2: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00a7: stloc.2 + IL_00a8: ldloca.s V_2 + IL_00aa: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00af: brtrue.s IL_00f4 + IL_00b1: ldarg.0 + IL_00b2: ldc.i4.0 + IL_00b3: dup + IL_00b4: stloc.0 + IL_00b5: stfld "int Program.d__1.<>1__state" + IL_00ba: ldarg.0 + IL_00bb: ldloc.2 + IL_00bc: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00c1: ldarg.0 + IL_00c2: stloc.s V_4 + IL_00c4: ldarg.0 + IL_00c5: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00ca: ldloca.s V_2 + IL_00cc: ldloca.s V_4 + IL_00ce: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.d__1)" + IL_00d3: leave IL_017e + IL_00d8: ldarg.0 + IL_00d9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00de: stloc.2 + IL_00df: ldarg.0 + IL_00e0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00e5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00eb: ldarg.0 + IL_00ec: ldc.i4.m1 + IL_00ed: dup + IL_00ee: stloc.0 + IL_00ef: stfld "int Program.d__1.<>1__state" + IL_00f4: ldloca.s V_2 + IL_00f6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00fb: ldarg.0 + IL_00fc: ldc.i4.2 + IL_00fd: stfld "int Program.d__1.<>2__current" + IL_0102: ldarg.0 + IL_0103: ldc.i4.s -5 + IL_0105: dup + IL_0106: stloc.0 + IL_0107: stfld "int Program.d__1.<>1__state" + IL_010c: leave.s IL_0172 + IL_010e: ldarg.0 + IL_010f: ldc.i4.m1 + IL_0110: dup + IL_0111: stloc.0 + IL_0112: stfld "int Program.d__1.<>1__state" + IL_0117: ldarg.0 + IL_0118: ldfld "bool Program.d__1.<>w__disposeMode" + IL_011d: pop + IL_011e: leave.s IL_014b + } + catch System.Exception + { + IL_0120: stloc.s V_5 + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.d__1.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldc.i4.0 + IL_012c: stfld "int Program.d__1.<>2__current" + IL_0131: ldarg.0 + IL_0132: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0137: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_013c: ldarg.0 + IL_013d: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0142: ldloc.s V_5 + IL_0144: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0149: leave.s IL_017e + } + IL_014b: ldarg.0 + IL_014c: ldc.i4.s -2 + IL_014e: stfld "int Program.d__1.<>1__state" + IL_0153: ldarg.0 + IL_0154: ldc.i4.0 + IL_0155: stfld "int Program.d__1.<>2__current" + IL_015a: ldarg.0 + IL_015b: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0160: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0165: ldarg.0 + IL_0166: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_016b: ldc.i4.0 + IL_016c: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_0171: ret + IL_0172: ldarg.0 + IL_0173: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0178: ldc.i4.1 + IL_0179: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_017e: ret + } + """); } [Theory, CombinatorialData] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs index 1c2c8c2f59050..213a94c3598ea 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs @@ -19581,9 +19581,9 @@ static void Test2(object x, System.ArgIterator y) // (11,25): error CS1601: Cannot make reference to variable of type 'ArgIterator' // static object Test1(out System.ArgIterator x) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(11, 25), - // (8,25): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + // (8,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Test2(Test1(out var x1), x1); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.ArgIterator").WithLocation(8, 25), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 25), // (6,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // async void Test() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Test").WithLocation(6, 16) @@ -19630,12 +19630,12 @@ static void Test2(object x, System.ArgIterator y) // (12,25): error CS1601: Cannot make reference to variable of type 'ArgIterator' // static object Test1(out System.ArgIterator x) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(12, 25), - // (8,25): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + // (8,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Test2(Test1(out System.ArgIterator x1), x1); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(8, 25), - // (9,9): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "System.ArgIterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 25), + // (9,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var x = default(System.ArgIterator); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.ArgIterator").WithLocation(9, 9), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 9), // (9,13): warning CS0219: The variable 'x' is assigned but its value is never used // var x = default(System.ArgIterator); Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(9, 13), diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 72c7ab82be010..0420b6489981a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -359,6 +359,85 @@ void assertAttributeData(string name) } } + [Fact] + public void Span_SingleElement_TempsAreNotReused() + { + var source = """ + using System; + + class Program + { + static void Main() + { + M(1); + M(2); + } + + static void M(params Span span) + { + Console.Write(span[0]); + Console.Write(span.Length); + } + } + """; + + var verifier = CompileAndVerify( + source, + targetFramework: TargetFramework.Net80, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput("1121")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main", """ + { + // Code size 29 (0x1d) + .maxstack 1 + .locals init (int V_0, + int V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.Span..ctor(ref int)" + IL_0009: call "void Program.M(params System.Span)" + IL_000e: ldc.i4.2 + IL_000f: stloc.1 + IL_0010: ldloca.s V_1 + IL_0012: newobj "System.Span..ctor(ref int)" + IL_0017: call "void Program.M(params System.Span)" + IL_001c: ret + } + """); + + verifier = CompileAndVerify( + source, + targetFramework: TargetFramework.Net70, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput("1121")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main", """ + { + // Code size 41 (0x29) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: newobj "System.Span..ctor(int[])" + IL_000f: call "void Program.M(params System.Span)" + IL_0014: ldc.i4.1 + IL_0015: newarr "int" + IL_001a: dup + IL_001b: ldc.i4.0 + IL_001c: ldc.i4.2 + IL_001d: stelem.i4 + IL_001e: newobj "System.Span..ctor(int[])" + IL_0023: call "void Program.M(params System.Span)" + IL_0028: ret + } + """); + } + [Fact] public void String() { @@ -4663,9 +4742,7 @@ static void Test(params System.Span a) CompileAndVerify( comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } - : Verification.Skipped, + verify: Verification.Skipped, expectedOutput: ExpectedOutput(@" int int")).VerifyDiagnostics(); @@ -4703,9 +4780,7 @@ class C3 : C2 {} CompileAndVerify( comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } - : Verification.Skipped, + verify: Verification.Skipped, expectedOutput: ExpectedOutput(@" C2 C2")).VerifyDiagnostics(); @@ -15575,5 +15650,117 @@ static void Main() Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14) ); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_01() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params Func[] projections) => Console.Write(1); + public void Method(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_02() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public C(params Func[] projections) => Console.Write(1); + public C(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C>(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_03() + { + string source = """ +using System; +using System.Collections.Generic; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params IEnumerable> projections) => Console.Write(1); + public void Method(params IEnumerable>> projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj index 3276fae69d6c0..a79819fbe934a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs new file mode 100644 index 0000000000000..d3b9880ee2a9d --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/RefUnsafeInIteratorAndAsyncTests.cs @@ -0,0 +1,1099 @@ +// 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.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public class RefUnsafeInIteratorAndAsyncTests : CSharpTestBase +{ + private static string? IfSpans(string expectedOutput) + => ExecutionConditionUtil.IsDesktop ? null : expectedOutput; + + [Fact] + public void LangVersion_RefLocalInAsync() + { + var source = """ + using System.Threading.Tasks; + class C + { + async Task M(int x) + { + ref int y = ref x; + ref readonly int z = ref y; + await Task.Yield(); + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 17), + // (7,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int z = ref y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "z").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 26)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(source).VerifyEmitDiagnostics(); + } + + [Fact] + public void LangVersion_RefLocalInIterator() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M(int x) + { + ref int y = ref x; + ref readonly int z = ref y; + yield return x; + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 17), + // (7,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int z = ref y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "z").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 26)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(source).VerifyEmitDiagnostics(); + } + + [Fact] + public void LangVersion_RefLocalInIterator_IEnumerator() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerator M(int x) + { + ref int y = ref x; + ref readonly int z = ref y; + yield return x; + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 17), + // (7,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int z = ref y; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "z").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 26)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(source).VerifyEmitDiagnostics(); + } + + [Fact] + public void LangVersion_RefStructInAsync() + { + var source = """ + #pragma warning disable CS0219 // variable unused + using System.Threading.Tasks; + class C + { + async Task M() + { + R y = default; + scoped R z = default; + await Task.Yield(); + } + } + ref struct R { } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // R y = default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "R").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 9), + // (8,16): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // scoped R z = default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "R").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 16)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(source).VerifyEmitDiagnostics(); + } + + [Fact] + public void LangVersion_RefStructInIterator() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M(R r) + { + M(r); + yield return -1; + } + } + ref struct R { } + """; + + var expectedDiagnostics = new[] + { + // (6,11): error CS4007: Instance of type 'R' cannot be preserved across 'await' or 'yield' boundary. + // M(r); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "r").WithArguments("R").WithLocation(6, 11) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void LangVersion_RestrictedInAsync() + { + var source = """ + #pragma warning disable CS0219 // variable unused + using System.Threading.Tasks; + class C + { + async Task M() + { + System.TypedReference t = default; + await Task.Yield(); + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.TypedReference t = default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "System.TypedReference").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 9)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(source).VerifyEmitDiagnostics(); + } + + [Fact] + public void LangVersion_RestrictedInIterator() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M(System.TypedReference t) + { + t.GetHashCode(); + yield return -1; + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,9): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // t.GetHashCode(); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "t").WithArguments("System.TypedReference").WithLocation(6, 9) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Await_RefLocal_Across() + { + var source = """ + using System.Threading.Tasks; + class C + { + async Task M(int x) + { + ref int y = ref x; + await Task.Yield(); + System.Console.Write(y); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (8,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(8, 30)); + } + + [Fact] + public void Await_RefLocal_Across_Reassign() + { + var source = """ + using System.Threading.Tasks; + class C + { + static Task Main() => M(123, 456); + static async Task M(int x, int z) + { + ref int y = ref x; + await Task.Yield(); + y = ref z; + System.Console.Write(y); + } + } + """; + CompileAndVerify(source, expectedOutput: "456").VerifyDiagnostics(); + } + + [Fact] + public void Await_RefLocal_Between() + { + var source = """ + using System.Threading.Tasks; + class C + { + static Task Main() => M(123); + static async Task M(int x) + { + ref int y = ref x; + System.Console.Write(y); + await Task.Yield(); + } + } + """; + CompileAndVerify(source, expectedOutput: "123").VerifyDiagnostics(); + } + + [Fact] + public void Await_RefStruct_Across() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + async Task M(int x) + { + Span y = new(ref x); + await Task.Yield(); + Console.Write(y.ToString()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (9,23): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.ToString()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(9, 23)); + } + + [Fact] + public void Await_RefStruct_Across_Reassign() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static Task Main() => M(123, 456); + static async Task M(int x, int z) + { + Span y = new(ref x); + await Task.Yield(); + y = new(ref z); + Console.Write(y[0]); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("456"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void Await_RefStruct_Between() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static Task Main() => M(123); + static async Task M(int x) + { + Span y = new(ref x); + Console.Write(y[0]); + await Task.Yield(); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("123"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void Await_Restricted_Across() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + async Task M() + { + TypedReference y = default; + await Task.Yield(); + Console.Write(y.GetHashCode()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (9,23): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.GetHashCode()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.TypedReference").WithLocation(9, 23)); + } + + [Fact] + public void Await_Restricted_Across_Reassign() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + TypedReference y = default; + await Task.Yield(); + y = default; + Console.Write(y.GetHashCode()); + } + } + """; + CompileAndVerify(source, expectedOutput: "0").VerifyDiagnostics(); + } + + [Fact] + public void Await_Restricted_Between() + { + var source = """ + using System; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + TypedReference y = default; + Console.Write(y.GetHashCode()); + await Task.Yield(); + } + } + """; + CompileAndVerify(source, expectedOutput: "0").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefLocal_Across() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M(int x) + { + ref int y = ref x; + yield return 1; + System.Console.Write(y); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (8,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(8, 30)); + } + + [Fact] + public void YieldReturn_RefLocal_Across_Indexer() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable this[int x] + { + get + { + ref int y = ref x; + yield return 1; + System.Console.Write(y); + } + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (10,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(10, 34)); + } + + [Fact] + public void YieldReturn_RefLocal_Across_NestedBlock() + { + var source = """ + using System.Collections.Generic; + class C + { + IEnumerable M(int x) + { + ref int y = ref x; + if (x != 0) { yield return 1; } + System.Console.Write(y); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (8,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(8, 30)); + } + + [Fact] + public void YieldReturn_RefLocal_Across_Async() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + async IAsyncEnumerable M(int x) + { + ref int y = ref x; + yield return 1; await Task.Yield(); + System.Console.Write(y); + } + } + """ + AsyncStreamsTypes; + CreateCompilationWithTasksExtensions(source).VerifyEmitDiagnostics( + // (9,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(9, 30)); + } + + [Fact] + public void YieldReturn_RefLocal_Across_Reassign() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M(123, 456)) + { + Console.Write(i + " "); + } + } + static IEnumerable M(int x, int z) + { + ref int y = ref x; + yield return -1; + y = ref z; + Console.Write(y); + } + } + """; + CompileAndVerify(source, expectedOutput: "-1 456").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefLocal_Across_Reassign_Indexer() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in new C()[123, 456]) + { + Console.Write(i + " "); + } + } + IEnumerable this[int x, int z] + { + get + { + ref int y = ref x; + yield return -1; + y = ref z; + Console.Write(y); + } + } + } + """; + CompileAndVerify(source, expectedOutput: "-1 456").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefLocal_Across_Reassign_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var i in M(123, 456)) + { + Console.Write(i + " "); + } + } + static async IAsyncEnumerable M(int x, int z) + { + ref int y = ref x; + yield return -1; await Task.Yield(); + y = ref z; + Console.Write(y); + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "-1 456").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefLocal_Between() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M(123)) + { + Console.Write(i + " "); + } + } + static IEnumerable M(int x) + { + ref int y = ref x; + Console.Write(y); + yield return -1; + } + } + """; + CompileAndVerify(source, expectedOutput: "123-1").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefLocal_Between_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var i in M(123)) + { + Console.Write(i + " "); + } + } + static async IAsyncEnumerable M(int x) + { + ref int y = ref x; + Console.Write(y); + yield return -1; await Task.Yield(); + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "123-1").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefStruct_Across() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + IEnumerable M(int x) + { + Span y = new(ref x); + yield return -1; + Console.Write(y.ToString()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (9,23): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.ToString()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(9, 23)); + } + + [Fact] + public void YieldReturn_RefStruct_Across_Indexer() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + IEnumerable this[int x] + { + get + { + Span y = new(ref x); + yield return -1; + Console.Write(y.ToString()); + } + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (11,27): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.ToString()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(11, 27)); + } + + [Fact] + public void YieldReturn_RefStruct_Across_NestedBlock() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + IEnumerable M(int x) + { + Span y = new(ref x); + if (x != 0) { yield return -1; } + Console.Write(y.ToString()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (9,23): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.ToString()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(9, 23)); + } + + [Fact] + public void YieldReturn_RefStruct_Across_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + async IAsyncEnumerable M(int x) + { + Span y = new(ref x); + yield return -1; await Task.Yield(); + Console.Write(y.ToString()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (10,23): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.ToString()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(10, 23)); + } + + [Fact] + public void YieldReturn_RefStruct_Across_Reassign() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M(123, 456)) + { + Console.Write(i + " "); + } + } + static IEnumerable M(int x, int z) + { + Span y = new(ref x); + yield return -1; + y = new(ref z); + Console.Write(y[0]); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("-1 456"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefStruct_Across_Reassign_Indexer() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in new C()[123, 456]) + { + Console.Write(i + " "); + } + } + IEnumerable this[int x, int z] + { + get + { + Span y = new(ref x); + yield return -1; + y = new(ref z); + Console.Write(y[0]); + } + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("-1 456"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefStruct_Across_Reassign_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var i in M(123, 456)) + { + Console.Write(i + " "); + } + } + static async IAsyncEnumerable M(int x, int z) + { + Span y = new(ref x); + yield return -1; await Task.Yield(); + y = new(ref z); + Console.Write(y[0]); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("-1 456"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_RefStruct_Between() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M(123)) + { + Console.Write(i + " "); + } + } + static IEnumerable M(int x) + { + Span y = new(ref x); + Console.Write(y[0]); + yield return -1; + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("123-1"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_Restricted_Across() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + IEnumerable M() + { + TypedReference y = default; + yield return -1; + Console.Write(y.GetHashCode()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (9,23): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.GetHashCode()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.TypedReference").WithLocation(9, 23)); + } + + [Fact] + public void YieldReturn_Restricted_Across_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + async IAsyncEnumerable M() + { + TypedReference y = default; + yield return -1; await Task.Yield(); + Console.Write(y.GetHashCode()); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (10,23): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y.GetHashCode()); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.TypedReference").WithLocation(10, 23)); + } + + [Fact] + public void YieldReturn_Restricted_Across_Reassign() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M()) + { + Console.Write(i + " "); + } + } + static IEnumerable M() + { + TypedReference y = default; + yield return -1; + y = default; + Console.Write(y.GetHashCode()); + } + } + """; + CompileAndVerify(source, expectedOutput: "-1 0").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_Restricted_Across_Reassign_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var i in M()) + { + Console.Write(i + " "); + } + } + static async IAsyncEnumerable M() + { + TypedReference y = default; + yield return -1; await Task.Yield(); + y = default; + Console.Write(y.GetHashCode()); + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "-1 0").VerifyDiagnostics(); + } + + [Fact] + public void YieldReturn_Restricted_Between() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var i in M()) + { + Console.Write(i + " "); + } + } + static IEnumerable M() + { + TypedReference y = default; + Console.Write(y.GetHashCode()); + yield return -1; + } + } + """; + CompileAndVerify(source, expectedOutput: "0-1").VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_RefLocal_Across() + { + var source = """ + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var a in M(10)) { throw null; } + foreach (var b in M(123)) { throw null; } + } + static IEnumerable M(int x) + { + ref int y = ref x; + if (x < 100) yield break; + System.Console.Write(y); + } + } + """; + CompileAndVerify(source, expectedOutput: "123").VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_RefLocal_Across_Async() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var a in M(10)) { throw null; } + await foreach (var b in M(123)) { throw null; } + } + static async IAsyncEnumerable M(int x) + { + ref int y = ref x; + if (x < 100) { await Task.Yield(); yield break; } + System.Console.Write(y); + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "123").VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_RefStruct_Across() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var a in M(10)) { throw null; } + foreach (var b in M(123)) { throw null; } + } + static IEnumerable M(int x) + { + Span y = new(ref x); + if (x < 100) yield break; + Console.Write(y[0]); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("123"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_RefStruct_Across_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var a in M(10)) { throw null; } + await foreach (var b in M(123)) { throw null; } + } + static async IAsyncEnumerable M(int x) + { + Span y = new(ref x); + if (x < 100) { await Task.Yield(); yield break; } + Console.Write(y[0]); + } + } + """; + CompileAndVerify(source, expectedOutput: IfSpans("123"), verify: Verification.FailsPEVerify, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_Restricted_Across() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + static void Main() + { + foreach (var a in M(10)) { throw null; } + foreach (var b in M(123)) { throw null; } + } + static IEnumerable M(int x) + { + TypedReference t = default; + if (x < 100) yield break; + Console.Write(x + t.GetHashCode()); + } + } + """; + CompileAndVerify(source, expectedOutput: "123").VerifyDiagnostics(); + } + + [Fact] + public void YieldBreak_Restricted_Across_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + class C + { + static async Task Main() + { + await foreach (var a in M(10)) { throw null; } + await foreach (var b in M(123)) { throw null; } + } + static async IAsyncEnumerable M(int x) + { + TypedReference t = default; + if (x < 100) { await Task.Yield(); yield break; } + Console.Write(x + t.GetHashCode()); + } + } + """ + AsyncStreamsTypes; + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "123").VerifyDiagnostics(); + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs index 58b403f3ed35f..0cdb4221d7784 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs @@ -241,9 +241,9 @@ static async Task Goo() }"; var comp = CreateCompilationWithMscorlib45(text, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( - // (8,27): error CS4007: 'await' cannot be used in an expression containing the type 'System.TypedReference' + // (8,27): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. // Console.WriteLine(new TypedReference().Equals(await Task.FromResult(0))); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await Task.FromResult(0)").WithArguments("System.TypedReference").WithLocation(8, 55)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new TypedReference()").WithArguments("System.TypedReference").WithLocation(8, 27)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 740346725b17a..8e550a2a565e8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -3218,9 +3218,9 @@ async Task M1(TypedReference tr) } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (7,34): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions + // (7,34): error CS4012: Parameters of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions // async Task M1(TypedReference tr) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "tr").WithArguments("System.TypedReference")); + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "tr").WithArguments("System.TypedReference")); } [Fact] @@ -3238,10 +3238,7 @@ async Task M1() await Task.Factory.StartNew(() => { }); } }"; - CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (9,9): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions - // TypedReference tr; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "TypedReference").WithArguments("System.TypedReference"), + CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( // (9,24): warning CS0168: The variable 'tr' is declared but never used // TypedReference tr; Diagnostic(ErrorCode.WRN_UnreferencedVar, "tr").WithArguments("tr")); @@ -3257,18 +3254,33 @@ public void BadSpecialByRefVarDeclLocal() class Test { async Task M1(bool truth) + { + var tr = new TypedReference(); // 1 + await Task.Factory.StartNew(() => { }); + } + + async Task M2(bool truth) { var tr = new TypedReference(); await Task.Factory.StartNew(() => { }); + var tr2 = tr; // 2 + } + + async Task M3() + { + var tr = new TypedReference(); + await Task.Factory.StartNew(() => { }); + tr = default; + var tr2 = tr; } }"; - CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (9,9): error CS4012: Parameters or locals of type 'TypedReference' cannot be declared in async methods or async lambda expressions. - // var tr = new TypedReference(); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.TypedReference").WithLocation(9, 9), + CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( // (9,13): warning CS0219: The variable 'tr' is assigned but its value is never used - // var tr = new TypedReference(); - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "tr").WithArguments("tr").WithLocation(9, 13)); + // var tr = new TypedReference(); // 1 + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "tr").WithArguments("tr").WithLocation(9, 13), + // (17,19): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // var tr2 = tr; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "tr").WithArguments("System.TypedReference").WithLocation(17, 19)); } [Fact] @@ -3288,9 +3300,6 @@ unsafe async public static void F() // (8,31): error CS0209: The type of a local declared in a fixed statement must be a pointer type // fixed (TypedReference tr) { } Diagnostic(ErrorCode.ERR_BadFixedInitType, "tr"), - // (8,16): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions. - // fixed (TypedReference tr) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "TypedReference").WithArguments("System.TypedReference"), // (8,31): error CS0210: You must provide an initializer in a fixed or using statement declaration // fixed (TypedReference tr) { } Diagnostic(ErrorCode.ERR_FixedMustInit, "tr"), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs index bb59d99fef196..fd120694a384c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs @@ -3531,10 +3531,7 @@ public async static Task Test() System.Console.Write(x); } } -}").VerifyDiagnostics( - // (20,26): error CS8177: Async methods cannot have by-reference locals - // foreach (ref int x in new E()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(20, 26)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3565,10 +3562,7 @@ public async static Task Test() System.Console.Write(x); } } -}").VerifyDiagnostics( - // (20,35): error CS8177: Async methods cannot have by-reference locals - // foreach (ref readonly int x in new E()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(20, 35)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3597,10 +3591,7 @@ public static IEnumerable Test() yield return x; } } -}").VerifyDiagnostics( - // (18,26): error CS8176: Iterators cannot have by-reference locals - // foreach (ref int x in new E()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(18, 26)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3629,10 +3620,7 @@ public static IEnumerable Test() yield return x; } } -}").VerifyDiagnostics( - // (18,35): error CS8176: Iterators cannot have by-reference locals - // foreach (ref readonly int x in new E()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(18, 35)); +}").VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs index 34be3f12005c3..06055448cc8b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs @@ -106,6 +106,168 @@ IEnumerable I() ); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + public class C + { + public async Task ProcessValueAsync() + { + await foreach (int item in GetValuesAsync()) + { + await Task.Yield(); + Console.Write(item); + } + } + + private async IAsyncEnumerable GetValuesAsync() + { + await Task.Yield(); + lock (this) + { + for (int i = 0; i < 10; i++) + { + yield return i; + + if (i == 3) + { + yield break; + } + } + } + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll.WithWarningLevel(8)); + CompileAndVerify(comp).VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (23,17): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return i; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(23, 17) + }; + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll.WithWarningLevel(9)); + CompileAndVerify(comp).VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll); + CompileAndVerify(comp).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Sync() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + + object o = new object(); + Console.WriteLine($"Before: {Monitor.IsEntered(o)}"); + using (IEnumerator e = GetValues(o).GetEnumerator()) + { + Console.WriteLine($"Inside: {Monitor.IsEntered(o)}"); + while (e.MoveNext()) + { + Console.WriteLine($"{e.Current}: {Monitor.IsEntered(o)}"); + } + Console.WriteLine($"Done: {Monitor.IsEntered(o)}"); + } + Console.WriteLine($"After: {Monitor.IsEntered(o)}"); + + static IEnumerable GetValues(object obj) + { + lock (obj) + { + for (int i = 0; i < 3; i++) + { + yield return i; + + if (i == 1) + { + yield break; + } + } + } + } + """; + + var expectedOutput = """ + Before: False + Inside: False + 0: True + 1: True + Done: False + After: False + """; + + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithWarningLevel(8), + expectedOutput: expectedOutput).VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (24,13): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return i; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(24, 13) + }; + + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithWarningLevel(9), + expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Nested() + { + var source = """ + using System.Collections.Generic; + + class C + { + IEnumerable M() + { + yield return 1; + lock (this) + { + yield return 2; + + local(); + + IEnumerable local() + { + yield return 3; + + lock (this) + { + yield return 4; + + yield break; + } + } + + yield break; + } + } + } + """; + + CreateCompilation(source).VerifyDiagnostics( + // (10,13): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(10, 13), + // (20,21): warning CS9237: 'yield return' should not be used in the body of a lock statement + // yield return 4; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(20, 21)); + } + [WorkItem(546081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546081")] [Fact] public void IteratorBlockWithUnreachableCode() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 9b2f16c772425..2b32616b85553 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -5981,15 +5981,15 @@ static void Main() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (10,39): error CS4012: Parameters or locals of type 'TypedReference' cannot be declared in async methods or async lambda expressions. + // (10,39): error CS4012: Parameters of type 'TypedReference' cannot be declared in async methods or async lambda expressions. // D1 d1 = async (TypedReference r) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "r").WithArguments("System.TypedReference").WithLocation(10, 39), - // (11,46): error CS4012: Parameters or locals of type 'RuntimeArgumentHandle' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "r").WithArguments("System.TypedReference").WithLocation(10, 39), + // (11,46): error CS4012: Parameters of type 'RuntimeArgumentHandle' cannot be declared in async methods or async lambda expressions. // D2 d2 = async (RuntimeArgumentHandle h) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 46), - // (12,36): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 46), + // (12,36): error CS4012: Parameters of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. // D3 d3 = async (ArgIterator i) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "i").WithArguments("System.ArgIterator").WithLocation(12, 36)); + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "i").WithArguments("System.ArgIterator").WithLocation(12, 36)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 74f8efa9b607c..7e4c694e57ba1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -2394,21 +2394,52 @@ public unsafe IEnumerable M4(int* a) // (33,44): error CS1637: Iterators cannot have pointer type parameters // public unsafe IEnumerable M4(int* a) Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(33, 44), - // (33,36): error CS1629: Unsafe code may not appear in iterators + // (33,36): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public unsafe IEnumerable M4(int* a) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "M4").WithLocation(33, 36), - // (37,40): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M4").WithArguments("ref and unsafe in async and iterator methods").WithLocation(33, 36), + // (37,40): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // IEnumerable Local(int* b) { yield break; } - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "int*").WithLocation(37, 40), - // (39,23): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(37, 40), + // (39,23): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Local(&x); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "&x").WithLocation(39, 23), - // (39,17): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(39, 23), + // (39,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Local(&x); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Local(&x)").WithLocation(39, 17), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Local(&x)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(39, 17), // (37,45): error CS1637: Iterators cannot have pointer type parameters // IEnumerable Local(int* b) { yield break; } Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "b").WithLocation(37, 45)); + + var expectedDiagnostics = new[] + { + // (8,37): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* a) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(8, 37), + // (17,41): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* x) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "x").WithLocation(17, 41), + // (27,37): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* a) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(27, 37), + // (33,44): error CS1637: Iterators cannot have pointer type parameters + // public unsafe IEnumerable M4(int* a) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(33, 44), + // (37,40): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // IEnumerable Local(int* b) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(37, 40), + // (37,45): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* b) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "b").WithLocation(37, 45), + // (39,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Local(&x); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Local(&x)").WithLocation(39, 17), + // (39,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Local(&x); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&x").WithLocation(39, 23) + }; + + CreateCompilation(src, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularNext.WithFeature("run-nullable-analysis", "never")).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(src, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview.WithFeature("run-nullable-analysis", "never")).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -3516,7 +3547,7 @@ static void Main() // (10,31): error CS0190: The __arglist construct is valid only within a variable argument method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_ArgsInvalid, "__arglist").WithLocation(10, 31), - // (18,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (18,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(18, 31), // (24,20): error CS1669: __arglist is not valid in this context @@ -3528,7 +3559,7 @@ static void Main() // (32,20): error CS1669: __arglist is not valid in this context // void Local(__arglist) Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(32, 20), - // (34,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (34,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(34, 31) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs index 320f13951a7f9..07bd155f06f33 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs @@ -4369,14 +4369,31 @@ static IEnumerable F() yield return sizeof(nuint); } }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( - // (6,22): error CS1629: Unsafe code may not appear in iterators + var expectedDiagnostics = new[] + { + // (6,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 22), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // yield return sizeof(nuint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nuint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(nint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nint)").WithLocation(6, 22), - // (7,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22), + // (7,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(nuint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nuint)").WithLocation(7, 22)); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nuint)").WithArguments("nuint").WithLocation(7, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index beb0e75552fa3..11f632233060e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -158691,5 +158691,45 @@ static void M() where T : struct, I // y.Value.Item.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Value.Item").WithLocation(15, 9)); } + + [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2045970")] + public void BadAsyncLambdaInNamedArgument() + { + var source = """ + #nullable enable + using System; + using System.Threading.Tasks; + + class D + { + void M() + { + var c1 = new C(); + var c2 = new C(); + C.M1(f: async () => + { + if (c2 != null) + { + c1. + await c2.M2(); + } + }); + } + } + + class C + { + public static void M1(Func f) { } + public async Task M2() => await Task.Yield(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (15,20): error CS1001: Identifier expected + // c1. + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(15, 20), + // (15,20): error CS1002: ; expected + // c1. + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(15, 20)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 0cd3ee133280d..e52cd58f53ebe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -4968,9 +4968,10 @@ public static unsafe void Test(TestStruct[] ar) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersionFacts.CSharpNext)] public void AwaitRefStruct(LanguageVersion languageVersion) { - CreateCompilation(@" + var comp = CreateCompilation(@" using System.Threading.Tasks; ref struct S { } @@ -4990,20 +4991,67 @@ async Task M(Task t) void M(S t, ref S t1) { } -}", parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.ReleaseDll).VerifyDiagnostics( - // (8,26): error CS0306: The type 'S' may not be used as a type argument - // async Task M(Task t) - Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), - // (12,9): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. - // var a = await t; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(12, 9), - // (14,9): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. - // var r = t.Result; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(14, 9), - // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // M(await t, ref r); - Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) - ); +}", parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.ReleaseDll); + if (languageVersion < LanguageVersionFacts.CSharpNext) + { + comp.VerifyDiagnostics( + // (8,26): error CS0306: The type 'S' may not be used as a type argument + // async Task M(Task t) + Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), + // (12,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var a = await t; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(12, 9), + // (14,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var r = t.Result; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(14, 9), + // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope + // M(await t, ref r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) + ); + } + else + { + comp.VerifyDiagnostics( + // (8,26): error CS0306: The type 'S' may not be used as a type argument + // async Task M(Task t) + Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), + // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope + // M(await t, ref r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) + ); + } + } + + [Fact] + public void AsyncLocals_Reassignment() + { + var code = """ + using System.Threading.Tasks; + class C + { + async Task M1() + { + int x = 42; + ref int y = ref x; + y.ToString(); + await Task.Yield(); + y.ToString(); // 1 + } + async Task M2() + { + int x = 42; + ref int y = ref x; + y.ToString(); + await Task.Yield(); + y = ref x; + y.ToString(); + } + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (10,9): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(10, 9)); } [WorkItem(25398, "https://github.com/dotnet/roslyn/issues/25398")] @@ -5344,9 +5392,9 @@ public static IEnumerable Iterator() { } """); compilation.VerifyEmitDiagnostics( - // (20,19): error CS4013: Instance of type 'PooledArrayHandle' cannot be used inside a nested function, query expression, iterator block or async method + // (20,19): error CS4007: Instance of type 'PooledArrayHandle' cannot be preserved across 'await' or 'yield' boundary. // using var handle = RentArray(200, out var array); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "handle = RentArray(200, out var array)").WithArguments("PooledArrayHandle").WithLocation(20, 19)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "handle = RentArray(200, out var array)").WithArguments("PooledArrayHandle").WithLocation(20, 19)); } [Theory(Skip = "https://github.com/dotnet/roslyn/issues/40583")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 825e55fd10807..7299939536559 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -1209,10 +1209,7 @@ async Task M() await Task.FromResult(false); } }"); - comp.VerifyDiagnostics( - // (7,26): error CS8177: Async methods cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(7, 26)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -1229,10 +1226,87 @@ IEnumerable M() yield return i; } }"); - comp.VerifyDiagnostics( - // (7,26): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(7, 26)); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefReadonlyInIterator_ForEach() + { + var source = """ + using System.Collections.Generic; + class C + { + int[] arr = new int[2]; + IEnumerable M() + { + ref readonly int[] x = ref arr; + + foreach (var i in x) + { + System.Console.Write(i); + yield return 1; + } + + foreach (var j in x) + { + System.Console.Write(j); + yield return 2; + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,28): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int[] x = ref arr; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 28)); + + var expectedDiagnostics = new[] + { + // (15,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in x) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "x").WithLocation(15, 27) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefReadonlyInIterator_ForEach_02() + { + var source = """ + using System.Collections.Generic; + + foreach (var i in new C().M()) System.Console.Write(i); + + class C + { + int[] arr = new[] { 4, 5 }; + public IEnumerable M() + { + ref readonly int[] x = ref arr; + + foreach (var i in x) + { + System.Console.Write(i); + yield return 1; + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (10,28): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int[] x = ref arr; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 28)); + + var expectedOutput = "4151"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -1247,7 +1321,7 @@ IEnumerable M() switch (this) { default: - ref readonly int x = ref (new int[1])[0]; // 1 + ref readonly int x = ref (new int[1])[0]; yield return 1; yield return x; @@ -1260,10 +1334,10 @@ void local() } } }"); - comp.VerifyDiagnostics( - // (10,34): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; // 1 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 34)); + comp.VerifyEmitDiagnostics( + // (12,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // yield return x; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "x").WithLocation(12, 30)); } [Fact] @@ -1278,22 +1352,19 @@ IEnumerable M() switch (this) { default: - ref readonly int x; // 1, 2 + ref readonly int x; // 1 yield return 1; - yield return x; // 3 + yield return x; // 2 break; } } }"); comp.VerifyDiagnostics( - // (10,34): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x; // 1, 2 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 34), // (10,34): error CS8174: A declaration of a by-reference variable must have an initializer - // ref readonly int x; // 1, 2 + // ref readonly int x; // 1 Diagnostic(ErrorCode.ERR_ByReferenceVariableMustBeInitialized, "x").WithLocation(10, 34), // (12,30): error CS0165: Use of unassigned local variable 'x' - // yield return x; // 3 + // yield return x; // 2 Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(12, 30)); } @@ -1316,9 +1387,9 @@ IEnumerable M() } }"); comp.VerifyDiagnostics( - // (10,43): error CS8176: Iterators cannot have by-reference locals + // (10,49): error CS1510: A ref or out value must be an assignable variable // foreach (ref readonly int x in (new int[1])) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 43)); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new int[1]").WithLocation(10, 49)); } [Fact] @@ -1331,18 +1402,15 @@ class C IEnumerable M() { if (true) - ref int x = ref (new int[1])[0]; // 1, 2 + ref int x = ref (new int[1])[0]; // 1 yield return 1; } }"); comp.VerifyDiagnostics( // (8,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13), - // (8,21): error CS8176: Iterators cannot have by-reference locals - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(8, 21)); + // ref int x = ref (new int[1])[0]; // 1 + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13)); } [Fact] @@ -1355,18 +1423,15 @@ class C async Task M() { if (true) - ref int x = ref (new int[1])[0]; // 1, 2 + ref int x = ref (new int[1])[0]; // 1 await Task.Yield(); } }"); comp.VerifyDiagnostics( // (8,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13), - // (8,21): error CS8177: Async methods cannot have by-reference locals - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(8, 21)); + // ref int x = ref (new int[1])[0]; // 1 + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13)); } [Fact] @@ -3087,14 +3152,147 @@ IEnumerable localFunction() } } }"; - - CreateCompilation(code).VerifyDiagnostics( - // (13,21): error CS8176: Iterators cannot have by-reference locals - // ref int z = ref x; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "z").WithLocation(13, 21), - // (8,17): error CS8176: Iterators cannot have by-reference locals + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // ref int y = ref x; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(8, 17)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 17), + // (13,21): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int z = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "z").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 21)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code).VerifyEmitDiagnostics(); + } + + [Fact] + public void RefLocal_Iterator_LocalFunction_01() + { + var code = """ + using System; + using System.Collections.Generic; + + int x = 5; + + foreach (var z in func()) + { + Console.Write(z); + x++; + } + + IEnumerable func() + { + ref int y = ref x; + yield return y; + y = ref x; + yield return y; + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (14,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(14, 13)); + + var expectedOutput = "56"; + + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefLocal_Iterator_LocalFunction_02() + { + var code = """ + using System.Collections.Generic; + + int x = 5; + + IEnumerable func() + { + ref int y = ref x; + yield return y; + yield return y; + } + + func(); + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 13)); + + var expectedDiagnostics = new[] + { + // (9,18): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // yield return y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(9, 18) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(code).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_Iterator_LocalFunction_01() + { + var code = """ + using System; + using System.Collections.Generic; + + int x = 5; + + foreach (var z in func()) + { + Console.Write(z); + x++; + } + + IEnumerable func() + { + Span y = new(ref x); + yield return y[0]; + y = new(ref x); + yield return y[0]; + } + """; + + var expectedOutput = ExecutionConditionUtil.IsDesktop ? null : "56"; + + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_Iterator_LocalFunction_02() + { + var code = """ + using System; + using System.Collections.Generic; + + int x = 5; + + IEnumerable func() + { + Span y = new(ref x); + yield return y[0]; + yield return y[0]; + } + + func(); + """; + + var expectedDiagnostics = new[] + { + // (10,18): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // yield return y[0]; + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(10, 18) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics(expectedDiagnostics); } [Fact, WorkItem(13073, "https://github.com/dotnet/roslyn/issues/13073")] @@ -3115,13 +3313,152 @@ await Task.Run(async () => }); } }"; - CreateCompilationWithMscorlib45(code).VerifyDiagnostics( - // (8,17): error CS8177: Async methods cannot have by-reference locals + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // ref int y = ref x; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(8, 17), - // (11,21): error CS8177: Async methods cannot have by-reference locals + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 17), + // (11,21): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // ref int z = ref x; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "z").WithLocation(11, 21)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "z").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 21)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code).VerifyEmitDiagnostics(); + } + + [Fact] + public void RefLocal_Async_LocalFunction_01() + { + var code = """ + using System; + using System.Threading.Tasks; + + int x = 5; + + await func(); + + async Task func() + { + ref int y = ref x; + Console.Write(y); + await Task.Yield(); + y = ref x; + Console.Write(y); + await Task.Yield(); + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (10,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 13)); + + var expectedOutput = "55"; + + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void RefLocal_Async_LocalFunction_02() + { + var code = """ + using System; + using System.Threading.Tasks; + + int x = 5; + + async Task func() + { + ref int y = ref x; + await Task.Yield(); + Console.Write(y); + } + + await func(); + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int y = ref x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 13)); + + var expectedDiagnostics = new[] + { + // (10,19): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(10, 19) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(code).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefStruct_Async_LocalFunction_01() + { + var code = """ + using System; + using System.Threading.Tasks; + + int x = 5; + + await func(); + + async Task func() + { + Span y = new(ref x); + Console.Write(y[0]); + await Task.Yield(); + y = new(ref x); + Console.Write(y[0]); + await Task.Yield(); + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (10,5): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Span y = new(ref x); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Span").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 5)); + + var expectedOutput = ExecutionConditionUtil.IsDesktop ? null : "55"; + + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + public void RefStruct_Async_LocalFunction_02() + { + var code = """ + using System; + using System.Threading.Tasks; + + int x = 5; + + async Task func() + { + Span y = new(ref x); + await Task.Yield(); + Console.Write(y[0]); + } + + await func(); + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (8,5): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Span y = new(ref x); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Span").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 5)); + + var expectedDiagnostics = new[] + { + // (10,19): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // Console.Write(y[0]); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(10, 19) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics(expectedDiagnostics); } [Fact, WorkItem(13073, "https://github.com/dotnet/roslyn/issues/13073")] @@ -3434,15 +3771,40 @@ static async void Goo() } "; - CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (8,17): error CS8177: Async methods cannot have by-reference locals - // ref int i = ref field; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "i").WithLocation(8, 17), + CreateCompilationWithMscorlib46(text).VerifyEmitDiagnostics( // (6,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // static async void Goo() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo").WithLocation(6, 23)); } + [Fact] + public void RefReturnAcrossAwaitExpression() + { + var code = """ + using System.Threading.Tasks; + + class Program + { + static int field = 1; + + static async Task Main() + { + M1(ref GiveMeRef(), await M2()); + } + + static void M1(ref int i, int j) { } + + static ref int GiveMeRef() => ref field; + + static Task M2() => Task.FromResult(field++); + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (9,16): error CS8178: A reference returned by a call to 'Program.GiveMeRef()' cannot be preserved across 'await' or 'yield' boundary. + // M1(ref GiveMeRef(), await M2()); + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GiveMeRef()").WithArguments("Program.GiveMeRef()").WithLocation(9, 16)); + } + [Fact] public void BadRefLocalInIteratorMethod() { @@ -3461,10 +3823,7 @@ static IEnumerable ObjEnumerable() } "; - CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (10,17): error CS8931: Iterators cannot have by-reference locals - // ref int i = ref field; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "i").WithLocation(10, 17)); + CreateCompilationWithMscorlib46(text).VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index fc678a39411b1..d361b612ede78 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -7269,15 +7269,15 @@ static void Main() }"; var comp = CreateCompilationWithMscorlib40AndSystemCore(source); comp.VerifyDiagnostics( - // (10,14): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (10,14): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // f = x=>f(__arglist); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle"), - // (11,29): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(10, 14), + // (11,29): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // f = delegate { return f(__arglist); }; - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle"), - // (12,44): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 29), + // (12,44): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // var q = from x in new int[10] select f(__arglist); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle") + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(12, 44) ); } @@ -7304,12 +7304,12 @@ static void Main() } }"; CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( - // (10,34): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (10,34): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // RuntimeArgumentHandle h2 = h; // Bad use of h - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle"), - // (11,43): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(10, 34), + // (11,43): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // ArgIterator args1 = new ArgIterator(h); // Bad use of h - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle")); + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 43)); } [Fact] @@ -7321,9 +7321,9 @@ public void CS4013ERR_SpecialByRefInLambda03() public class C { static void N(RuntimeArgumentHandle x) {} - static IEnumerable M(RuntimeArgumentHandle h1) // Error: hoisted to field + static IEnumerable M(RuntimeArgumentHandle h1) { - N(h1); + N(h1); // Error: hoisted to field yield return 1; RuntimeArgumentHandle h2 = default(RuntimeArgumentHandle); yield return 2; @@ -7339,12 +7339,12 @@ static void Main() CreateCompilation(source).Emit(new System.IO.MemoryStream()).Diagnostics .Verify( - // (7,51): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method - // static IEnumerable M(RuntimeArgumentHandle h1) // Error: hoisted to field - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h1").WithArguments("System.RuntimeArgumentHandle"), - // (13,7): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (9,7): error CS4007: Instance of type 'System.RuntimeArgumentHandle' cannot be preserved across 'await' or 'yield' boundary. + // N(h1); // Error: hoisted to field + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "h1").WithArguments("System.RuntimeArgumentHandle").WithLocation(9, 7), + // (13,7): error CS4007: Instance of type 'System.RuntimeArgumentHandle' cannot be preserved across 'await' or 'yield' boundary. // N(h2); // Error: hoisted to field - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h2").WithArguments("System.RuntimeArgumentHandle") + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "h2").WithArguments("System.RuntimeArgumentHandle").WithLocation(13, 7) ); } @@ -15318,35 +15318,48 @@ class C { IEnumerator IteratorMeth() { int i; - unsafe // CS1629 + unsafe { int *p = &i; yield return *p; } } - unsafe IEnumerator IteratorMeth2() { // CS1629 + unsafe IEnumerator IteratorMeth2() { yield break; } } "; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (7,7): error CS1629: Unsafe code may not appear in iterators - // unsafe // CS1629 - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe"), - // (9,10): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,7): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 7), + // (9,10): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int *p = &i; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "int *"), - // (9,19): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 10), + // (9,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int *p = &i; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "&i"), - // (10,24): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 19), + // (10,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return *p; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "p"), - // (14,29): error CS1629: Unsafe code may not appear in iterators - // unsafe IEnumerator IteratorMeth2() { // CS1629 - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "IteratorMeth2") + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 24), + // (14,29): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe IEnumerator IteratorMeth2() { + Diagnostic(ErrorCode.ERR_FeatureInPreview, "IteratorMeth2").WithArguments("ref and unsafe in async and iterator methods").WithLocation(14, 29) ); + + var expectedDiagnostics = new[] + { + // (9,20): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &i; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "i").WithLocation(9, 20), + // (10,10): error CS9238: Cannot use 'yield return' in an 'unsafe' block + // yield return *p; + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(10, 10) + }; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index 922ac7985d750..e3e2312163fa8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -975,9 +975,9 @@ public static async Task M1(Span arg) CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); comp.VerifyDiagnostics( - // (11,48): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. + // (11,48): error CS4012: Parameters of type 'Span' cannot be declared in async methods or async lambda expressions. // public static async Task M1(Span arg) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "arg").WithArguments("System.Span").WithLocation(11, 48) + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "arg").WithArguments("System.Span").WithLocation(11, 48) ); } @@ -996,48 +996,77 @@ static void Main() public static async Task M1() { - Span local1 = default(Span); // 1 - var local2 = default(Span); // 2 + Span local1 = default(Span); + var local2 = default(Span); await Task.Yield(); return 42; } + + public static async Task M2() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local1.Length; // 1 + } + + public static async Task M3() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local2.Length; // 2 + } + + public static async Task M4() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local1.Length + local2.Length; // 3, 4 + } } "; - CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - - comp.VerifyDiagnostics( - // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), + var expectedDiagnostics = new[] + { // (13,19): warning CS0219: The variable 'local1' is assigned but its value is never used - // Span local1 = default(Span); // 1 + // Span local1 = default(Span); Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(13, 19), - // (14,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(14, 9), // (14,13): warning CS0219: The variable 'local2' is assigned but its value is never used - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13) - ); + // var local2 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13), + // (23,13): warning CS0219: The variable 'local2' is assigned but its value is never used + // var local2 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(23, 13), + // (26,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(26, 16), + // (31,19): warning CS0219: The variable 'local1' is assigned but its value is never used + // Span local1 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(31, 19), + // (35,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(35, 16), + // (44,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length + local2.Length; // 3, 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(44, 16), + // (44,32): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length + local2.Length; // 3, 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(44, 32) + }; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), - // (13,19): warning CS0219: The variable 'local1' is assigned but its value is never used - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(13, 19), - // (14,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(14, 9), - // (14,13): warning CS0219: The variable 'local2' is assigned but its value is never used - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13) - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] @@ -1051,27 +1080,44 @@ public class Program { public static async Task M1() { - M2(out var local1); // 1 - M2(out Span local2); // 2 + M2(out var local1); + M2(out Span local2); await Task.Yield(); return 42; } + public static async Task M3() + { + M2(out var local1); + M2(out Span local2); + + await Task.Yield(); + return local1.Length; // 1 + } + + public static async Task M4() + { + M2(out var local1); + M2(out Span local2); + + await Task.Yield(); + return local2.Length; // 2 + } + static void M2(out Span s) => throw null; } "; CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyDiagnostics( - // (9,16): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // M2(out var local1); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(9, 16), - // (10,16): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // M2(out Span local2); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(10, 16) - ); + comp.VerifyEmitDiagnostics( + // (22,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(22, 16), + // (31,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(31, 16)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1086,30 +1132,49 @@ public class Program { public async Task M(IReadOnlyList o) { - foreach (ReadOnlySpan c1 in o) { } // 1 + foreach (ReadOnlySpan c1 in o) { } var enumerator = ((IEnumerable)o).GetEnumerator(); while (enumerator.MoveNext()) { - ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; // 2 + ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; _ = c2.Length; } await Task.Yield(); return; } + + public async Task M2(IReadOnlyList o) + { + foreach (ReadOnlySpan c1 in o) + { + await Task.Yield(); + _ = c1.Length; // 1 + } + + var enumerator = ((IEnumerable)o).GetEnumerator(); + while (enumerator.MoveNext()) + { + ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; + await Task.Yield(); + _ = c2.Length; // 2 + } + + await Task.Yield(); + return; + } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (10,18): error CS4012: Parameters or locals of type 'ReadOnlySpan' cannot be declared in async methods or async lambda expressions. - // foreach (ReadOnlySpan c1 in o) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(10, 18), - // (15,13): error CS4012: Parameters or locals of type 'ReadOnlySpan' cannot be declared in async methods or async lambda expressions. - // ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(15, 13) - ); + comp.VerifyEmitDiagnostics( + // (28,17): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // _ = c1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c1").WithArguments("System.ReadOnlySpan").WithLocation(28, 17), + // (36,17): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // _ = c2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c2").WithArguments("System.ReadOnlySpan").WithLocation(36, 17)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1123,11 +1188,23 @@ public class Program { public async Task M() { - (Span s1, Span s2) = new Program(); // 1, 2 - var (s3, s4) = new Program(); // 3, 4 - (var s5, var s6) = new Program(); // 5, 6 + (Span s1, Span s2) = new Program(); + var (s3, s4) = new Program(); + (var s5, var s6) = new Program(); + + await Task.Yield(); + return; + } + + public async Task M2() + { + (Span s1, Span s2) = new Program(); + var (s3, s4) = new Program(); + (var s5, var s6) = new Program(); await Task.Yield(); + + _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 return; } @@ -1135,26 +1212,16 @@ public async Task M() } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (9,10): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (Span s1, Span s2) = new Program(); // 1, 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(9, 10), - // (9,24): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (Span s1, Span s2) = new Program(); // 1, 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(9, 24), - // (10,14): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var (s3, s4) = new Program(); // 3, 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s3").WithArguments("System.Span").WithLocation(10, 14), - // (10,18): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var (s3, s4) = new Program(); // 3, 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s4").WithArguments("System.Span").WithLocation(10, 18), - // (11,10): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (var s5, var s6) = new Program(); // 5, 6 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(11, 10), - // (11,18): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (var s5, var s6) = new Program(); // 5, 6 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(11, 18) - ); + comp.VerifyEmitDiagnostics( + // (25,13): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1").WithArguments("System.Span").WithLocation(25, 13), + // (25,25): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s4").WithArguments("System.Span").WithLocation(25, 25), + // (25,37): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s5").WithArguments("System.Span").WithLocation(25, 37)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1167,11 +1234,26 @@ public class Program { public async Task M() { - using (default(RS)) { } // 1 - using (var s1 = default(RS)) { } // 2 - using (RS s2 = default(RS)) { } // 3 - using RS s3 = default(RS); // 4 - using var s4 = default(RS); // 5 + using (default(RS)) { } + using (var s1 = default(RS)) { } + using (RS s2 = default(RS)) { } + using RS s3 = default(RS); // 1 + using var s4 = default(RS); // 2 + + await Task.Yield(); + return; + } + + public async Task M2() + { + using (default(RS)) { await Task.Yield(); } // 3 + using (var s1 = default(RS)) { await Task.Yield(); } // 4 + using (RS s2 = default(RS)) { await Task.Yield(); } // 5 + { + using RS s3 = default(RS); // 6 + await Task.Yield(); + using var s4 = default(RS); + } await Task.Yield(); return; @@ -1184,23 +1266,25 @@ public void Dispose() { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (8,16): error CS9104: A using statement resource of type 'RS' cannot be used in async methods or async lambda expressions. - // using (default(RS)) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefUsing, "default(RS)").WithArguments("RS").WithLocation(8, 16), - // (9,16): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using (var s1 = default(RS)) { } // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("RS").WithLocation(9, 16), - // (10,16): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using (RS s2 = default(RS)) { } // 3 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "RS").WithArguments("RS").WithLocation(10, 16), - // (11,15): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using RS s3 = default(RS); // 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "RS").WithArguments("RS").WithLocation(11, 15), - // (12,15): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using var s4 = default(RS); // 5 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("RS").WithLocation(12, 15) - ); + comp.VerifyEmitDiagnostics( + // (11,18): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using RS s3 = default(RS); // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s3 = default(RS)").WithArguments("RS").WithLocation(11, 18), + // (12,19): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using var s4 = default(RS); // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s4 = default(RS)").WithArguments("RS").WithLocation(12, 19), + // (20,16): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (default(RS)) { await Task.Yield(); } // 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(RS)").WithArguments("RS").WithLocation(20, 16), + // (21,20): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (var s1 = default(RS)) { await Task.Yield(); } // 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1 = default(RS)").WithArguments("RS").WithLocation(21, 20), + // (22,19): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (RS s2 = default(RS)) { await Task.Yield(); } // 5 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s2 = default(RS)").WithArguments("RS").WithLocation(22, 19), + // (24,22): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using RS s3 = default(RS); // 6 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s3 = default(RS)").WithArguments("RS").WithLocation(24, 22)); } [Fact] @@ -1214,24 +1298,76 @@ public class Program { public async Task M() { - if (M2() is var s1) { } // 1 - if (M2() is Span s2) { } // 2 + if (M2() is var s1) { } + if (M2() is Span s2) { } + if (M2() is var s3) { await Task.Yield(); } + if (M2() is Span s4) { await Task.Yield(); } + + await Task.Yield(); + return; + } + + public async Task M3() + { + if (M2() is var s1) + { + await Task.Yield(); + _ = s1.Length; // 1 + } + if (M2() is Span s2) + { + await Task.Yield(); + _ = s2.Length; // 2 + } await Task.Yield(); return; } + static Span M2() => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (9,25): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // if (M2() is var s1) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s1").WithArguments("System.Span").WithLocation(9, 25), - // (10,21): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // if (M2() is Span s2) { } // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(10, 21) - ); + comp.VerifyEmitDiagnostics( + // (23,17): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length; + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1").WithArguments("System.Span").WithLocation(23, 17), + // (28,17): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s2.Length; + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s2").WithArguments("System.Span").WithLocation(28, 17)); + } + + [Fact] + public void AsyncLocals_Reassignment() + { + var code = """ + using System; + using System.Threading.Tasks; + class C + { + async Task M1() + { + int x = 42; + Span y = new(ref x); + y.ToString(); + await Task.Yield(); + y.ToString(); // 1 + } + async Task M2() + { + int x = 42; + Span y = new(ref x); + y.ToString(); + await Task.Yield(); + y = new(ref x); + y.ToString(); + } + } + """; + CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (11,9): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(11, 9)); } [Fact] @@ -1280,19 +1416,18 @@ public static async Task I1() CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyEmitDiagnostics( - // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + var expectedDiagnostics = new[] + { + // (17,19): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. // TakesSpan(default(Span), await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(Span)").WithArguments("System.Span").WithLocation(17, 19) + }; + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyEmitDiagnostics( - // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' - // TakesSpan(default(Span), await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] @@ -1331,19 +1466,49 @@ public static async Task I1() CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyEmitDiagnostics( - // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'Span' + var expectedDiagnostics = new[] + { + // (14,22): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. // TakesSpan(s: default(Span), i: await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(Span)").WithArguments("System.Span").WithLocation(14, 22) + }; + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyEmitDiagnostics( - // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'Span' - // TakesSpan(s: default(Span), i: await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void AwaitAssignSpan() + { + var source = """ + using System; + using System.Threading.Tasks; + + ReadOnlySpan r = await M(); + Console.Write(r[1]); + + async Task M() + { + await Task.Yield(); + return new[] { 4, 5, 6 }; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,1): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ReadOnlySpan r = await M(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ReadOnlySpan").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 1)); + + var expectedOutput = "5"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs index d96e6abf25f3b..751ca8595562d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs @@ -757,13 +757,29 @@ async void M() comp.VerifyDiagnostics( // (8,38): error CS0150: A constant value is expected // Span p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_ConstantExpected, "await Task.FromResult(1)").WithLocation(8, 38), - // (8,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(8, 9) + Diagnostic(ErrorCode.ERR_ConstantExpected, "await Task.FromResult(1)").WithLocation(8, 38) ); } + [Fact] + public void TestAwait_Span_02() + { + var comp = CreateCompilationWithMscorlibAndSpan(""" + using System; + using System.Threading.Tasks; + class Test + { + static async Task Main() + { + Span p = stackalloc int[1] { await Task.FromResult(2) }; + Console.WriteLine(p[0]); + } + } + """, TestOptions.UnsafeReleaseExe); + + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + [Fact] public void TestSelfInSize() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs index fd299709aedbf..44615f646a7e6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs @@ -1113,13 +1113,24 @@ public void LocalDeclarationStatement_18() await System.Threading.Tasks.Task.Yield(); "; + var expectedDiagnostics = new[] + { + // (3,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int d = ref c; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "d").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 9) + }; + var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); + comp.VerifyDiagnostics(expectedDiagnostics); - comp.VerifyDiagnostics( - // (3,9): error CS8177: Async methods cannot have by-reference locals - // ref int d = ref c; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "d").WithLocation(3, 9) - ); + comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(text, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 12753c86accd8..ccfc049d18034 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -275,67 +275,2326 @@ unsafe System.Collections.Generic.IEnumerator Goo() } "; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Goo")); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerator Goo() + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Goo").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void IteratorUnsafe3() + { + var text = @" +class C +{ + System.Collections.Generic.IEnumerator Goo() + { + unsafe { } + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void IteratorUnsafe4() + { + var text = @" +unsafe class C +{ + System.Collections.Generic.IEnumerator Goo() + { + unsafe { } + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [WorkItem(546657, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546657")] + [Fact] + public void IteratorUnsafe5() + { + var text = @" +unsafe class C +{ + System.Collections.Generic.IEnumerator Goo() + { + System.Action a = () => { unsafe { } }; + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Action a = () => { unsafe { } }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 35)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73280")] + public void Iterator_UnsafeBlock_LangVersion() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M1() + { + unsafe // langversion error in C# 12 + { + int* p = null; // unnecessary langversion error in C# 12 + } + yield break; + } + System.Collections.Generic.IEnumerable M2() + { + int* p = null; // necessary langversion error in C# 12 + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe // langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9), + // (7,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; // unnecessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 13), + // (13,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; // necessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 9)); + + var expectedDiagnostics = new[] + { + // (13,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* p = null; // necessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(13, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Local() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9), + // (8,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 13), + // (8,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 22), + // (9,14): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 14), + // (9,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 19)); + + var expectedDiagnostics = new[] + { + // (8,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Parameter() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M(int x) + { + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9), + // (7,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 13), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22), + // (8,14): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 14), + // (8,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 19)); + + var expectedDiagnostics = new[] + { + // (7,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(7, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_01() + { + var code = """ + class Program + { + static int _f; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + fixed (int* p = &_f) { } + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_02() + { + var code = """ + #pragma warning disable CS0649 // field is never assigned to + unsafe class Program + { + static int* _p; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + int* p = _p; + p = &p[1]; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_03() + { + var code = """ + struct S + { + public int F; + } + class Program + { + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + S s = default; + int* p = &s.F; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (12,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int* p = &s.F; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "s.F").WithLocation(12, 23)); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_04() + { + var code = """ + class Program + { + static int _f; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + int* p = &_f; + (*p)++; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (8,22): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // int* p = &_f; + Diagnostic(ErrorCode.ERR_FixedNeeded, "&_f").WithLocation(8, 22)); + } + + [Fact] + public void Iterator_UnsafeBlock_SizeOf() + { + var code = """ + foreach (var x in new C().M()) System.Console.Write(x); + + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x; + unsafe + { + x = sizeof(nint); + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (8,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 9), + // (10,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // x = sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 17)); + + var expectedOutput = IntPtr.Size.ToString(); + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_YieldBreak() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + unsafe + { + yield break; + } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_YieldReturn() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + unsafe + { + yield return 1; + } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + var expectedDiagnostics = new[] + { + // (7,13): error CS9238: Cannot use 'yield return' in an 'unsafe' block + // yield return 1; + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(7, 13) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Async_01() + { + var code = """ + await foreach (var x in new C().M()) System.Console.Write(x); + + class C + { + public async System.Collections.Generic.IAsyncEnumerable M() + { + int x = 1; + await System.Threading.Tasks.Task.Yield(); + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(code, options: TestOptions.UnsafeReleaseExe); + CompileAndVerify(comp, expectedOutput: "1", verify: Verification.Fails).VerifyDiagnostics( + // (11,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(11, 23)); + } + + // Should behave equivalently to AwaitBetweenUnsafeBlocks. + [Fact] + public void Iterator_UnsafeBlock_Async_02() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + unsafe + { + int *p = &x; + if (*p == 3) yield break; + } + yield return x; + } + } + """; + + var expectedDiagnostics = new[] + { + // (8,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23), + // (14,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(14, 23), + // (20,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(20, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + // Should behave equivalently to Iterator_UnsafeBlock_Async_02. + [Fact] + public void AwaitBetweenUnsafeBlocks() + { + var code = """ + using System; + using System.Threading.Tasks; + + await new C().M(); + + class C + { + async Task Report(int x) + { + Console.Write(x); + await Task.Yield(); + } + public async Task M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + await Report(x); + unsafe + { + int *p = &x; + *p = *p + 1; + } + await Report(x); + unsafe + { + int *p = &x; + if (*p == 3) return; + } + await Report(x); + } + } + """; + var expectedDiagnostics = new[] + { + // (18,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(18, 23), + // (24,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(24, 23), + // (30,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(30, 23) + }; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_LocalFunction_Nested() + { + var code = """ + class Program + { + static void F() + { + unsafe + { + static System.Collections.Generic.IEnumerable G(int x) + { + unsafe + { + x = sizeof(nint); + } + yield return x; + + unsafe + { + G2(new int*[0]); + static System.Collections.Generic.IEnumerable G2(int*[] x) + { + int y; + unsafe + { + y = *x[0]; + } + yield return y; + } + } + } + G(0); + } + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LambdaInIterator_Async() + { + var code = """ + using System.Collections.Generic; + using System.Threading.Tasks; + unsafe class C + { + IEnumerable M() + { + yield return 1; + var lam = async () => await Task.Yield(); + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,31): error CS4004: Cannot await in an unsafe context + // var lam = async () => await Task.Yield(); + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(8, 31)); + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_Async() + { + var code = """ + using System.Collections.Generic; + using System.Threading.Tasks; + unsafe class C + { + IEnumerable M() + { + yield return 1; + local(); + async void local() { await Task.Yield(); } + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (9,30): error CS4004: Cannot await in an unsafe context + // async void local() { await Task.Yield(); } + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 30)); + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LambdaInIterator_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + var lam = () => sizeof(nint); + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lam = () => sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 25)); + + var expectedDiagnostics = new[] + { + // (6,25): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // var lam = () => sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 25) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChange() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + local(); + void local() { int* p = null; } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (7,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 24) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChangeWorkaround() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + local(); + unsafe void local() { int* p = null; } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChange_Property() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable P + { + get + { + yield return 1; + local(); + void local() { int* p = null; } + } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (9,28): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(9, 28) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChange_Operator() + { + var code = """ + unsafe class C + { + public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return 1; + local(); + void local() { int* p = null; } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (7,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 24) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChange_Nested_01() + { + var code = """ + unsafe class C + { + void M() + { + local1(); + System.Collections.Generic.IEnumerable local1() + { + yield return 1; + local2(); + void local2() { int* p = null; } + } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (10,29): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local2() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(10, 29) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_BreakingChange_Nested_02() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + local1(); + void local1() + { + local2(); + void local2() { int* p = null; } + } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (10,29): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local2() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(10, 29) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73280")] + public void UnsafeContext_LangVersion() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + int* p = null; + yield break; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + var expectedDiagnostics = new[] + { + // (5,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* p = null; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(5, 9) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Signature_Unsafe(bool unsafeClass, bool unsafeMethod) + { + if (!unsafeClass && !unsafeMethod) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} void M(int* p) { } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Signature_Safe() + { + var code = """ + class C + { + void M(int* p) { } + } + """; + + var expectedDiagnostics = new[] + { + // (3,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void M(int* p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Body_Unsafe(bool unsafeClass, bool unsafeMethod) + { + if (!unsafeClass && !unsafeMethod) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} int M() + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Body_Safe() + { + var code = """ + class C + { + int M() + { + return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,16): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 16) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Iterator_Signature_UnsafeMethod(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (3,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 56)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,51): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 51) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22)); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Safe_NestedBlock() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable M() + { + { + yield return sizeof(nint); + } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (6,26): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 26)); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Unsafe_NestedBlock() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + { + yield return local(); + } + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Iterator_Body_CSharp13(bool unsafeClass, bool unsafeMethod) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Iterator_Body_CSharp13_NestedBlock(bool unsafeClass, bool unsafeMethod) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} System.Collections.Generic.IEnumerable M() + { + { + yield return sizeof(nint); + } + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,26): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 26) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Signature_Unsafe(bool unsafeClass, bool unsafeOperator) + { + if (!unsafeClass && !unsafeOperator) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeOperator ? "unsafe" : "")}} public static C operator+(C c, int* p) + { + return c; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Signature_Safe() + { + var code = """ + class C + { + public static C operator+(C c, int* p) + { + return c; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,36): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // public static C operator+(C c, int* p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 36) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Body_Unsafe(bool unsafeClass, bool unsafeOperator) + { + if (!unsafeClass && !unsafeOperator) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeOperator ? "unsafe" : "")}} public static int operator+(C c1, C c2) + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Body_Safe() + { + var code = """ + class C + { + public static int operator+(C c1, C c2) + { + return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,16): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 16) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Iterator_Signature_UnsafeOperator(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + public static unsafe System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (3,78): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static unsafe System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "+").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 78)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Signature_Safe() + { + var code = """ + class C + { + public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,78): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 78) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22)); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Iterator_Body_CSharp13(bool unsafeClass, bool unsafeIndexer) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Signature_Unsafe(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} int this[int* p] + { + get { return 0; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Signature_Safe() + { + var code = """ + class C + { + int this[int* p] + { + get { return 0; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int this[int* p] + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 14) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Body_Unsafe(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} int this[int x] + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Body_Safe() + { + var code = """ + class C + { + int this[int x] + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Iterator_Signature_UnsafeIndexer(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // get { yield break; } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "get").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,54): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable this[int*[] p] + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 54) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15)); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get + { + yield return local(); + int local() => sizeof(nint); + } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Iterator_Body_CSharp13_UnsafeSetter(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp13_SafeSetter() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Signature_Unsafe(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} int* P + { + get { throw null; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Signature_Safe() + { + var code = """ + class C + { + int* P + { + get { throw null; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* P + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 5) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Body_Unsafe(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} int P + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Body_Safe() + { + var code = """ + class C + { + int P + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Iterator_Signature_UnsafeProperty(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // get { yield break; } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "get").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,44): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable P + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 44) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15)); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable P + { + get + { + yield return local(); + int local() => sizeof(nint); + } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Iterator_Body_CSharp13_UnsafeSetter(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] - public void IteratorUnsafe3() + public void UnsafeContext_Property_Iterator_Body_CSharp13_SafeSetter() { - var text = @" -class C -{ - System.Collections.Generic.IEnumerator Goo() - { - unsafe { } - yield return 1; - } -} -"; + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Signature_Unsafe(bool unsafeBlock, bool unsafeFunction) + { + if (!unsafeBlock && !unsafeFunction) + { + return; + } + + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} void M(int* p) { } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); } [Fact] - public void IteratorUnsafe4() + public void UnsafeContext_LocalFunction_Signature_Safe() { - var text = @" -unsafe class C -{ - System.Collections.Generic.IEnumerator Goo() - { - unsafe { } - yield return 1; - } -} -"; + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + void M(int* p) { } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (2,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void M(int* p) { } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 8) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Body_Unsafe(bool unsafeBlock, bool unsafeFunction) + { + if (!unsafeBlock && !unsafeFunction) + { + return; + } + + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} int M() + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); } - [WorkItem(546657, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546657")] [Fact] - public void IteratorUnsafe5() + public void UnsafeContext_LocalFunction_Body_Safe() { - var text = @" -unsafe class C -{ - System.Collections.Generic.IEnumerator Goo() - { - System.Action a = () => { unsafe { } }; - yield return 1; - } -} -"; + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + int M() + { + return sizeof(nint); + } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (4,12): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(4, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Signature_UnsafeBlock() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + unsafe + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Iterator_Signature_UnsafeFunction(bool unsafeBlock) + { + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + unsafe System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Signature_Safe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + """; + + var expectedDiagnostics = new[] + { + // (2,47): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 47) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp12_Safe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (4,18): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(4, 18)); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + unsafe + { + System.Collections.Generic.IEnumerable M() + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp13(bool unsafeBlock, bool unsafeFunction) + { + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Signature_Unsafe() + { + var code = """ + unsafe + { + var lam = (int* p) => { }; + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Lambda_Signature_Safe() + { + var code = """ + var lam = (int* p) => { }; + """; + + var expectedDiagnostics = new[] + { + // (1,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int* p) => { }; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 12), + // (1,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int* p) => { }; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(1, 17) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Body_Unsafe() + { + var code = """ + unsafe + { + var lam = () => + { + return sizeof(nint); + }; + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Lambda_Body_Safe() + { + var code = """ + var lam = () => + { + return sizeof(nint); + }; + """; + + var expectedDiagnostics = new[] + { + // (3,12): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(3, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Signature_Unsafe() + { + var code = """ + unsafe + { + var lam = (int*[] p) => + { + yield break; + }; + } + """; + + var expectedDiagnostics = new[] + { + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield break; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Signature_Safe() + { + var code = """ + var lam = (int*[] p) => + { + yield break; + }; + """; + + var expectedDiagnostics = new[] + { + // (1,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int*[] p) => + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 12), + // (1,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int*[] p) => + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(1, 19), + // (3,5): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield break; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(3, 5) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Body_Unsafe() + { + var code = """ + unsafe + { + var lam = () => + { + yield return sizeof(nint); + }; + } + """; + + var expectedDiagnostics = new[] + { + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = [ + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9), + // (5,9): error CS9238: Cannot use 'yield return' in an 'unsafe' block + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(5, 9) + ]; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Body_Safe() + { + var code = """ + var lam = () => + { + yield return sizeof(nint); + }; + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(3, 5), + // (3,18): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(3, 18) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeBlock_InAsyncMethod() + { + var code = """ + using System.Threading.Tasks; + class C + { + async Task M(int x) + { + unsafe { x = sizeof(nint); } + await Task.Yield(); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); } [Fact] @@ -936,18 +3195,41 @@ public void UnsafeIteratorSignatures() Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p")); var withUnsafeOnMembers = string.Format(template, "", "unsafe"); - CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,64): error CS1637: Iterators cannot have pointer type parameters - Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p"), - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator")); //this is for putting "unsafe" on an iterator, not for the parameter type + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70), + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); //this is for putting "unsafe" on an iterator, not for the parameter type + + var expectedDiagnostics = new[] + { + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70) + }; + + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); var withUnsafeOnTypeAndMembers = string.Format(template, "unsafe", "unsafe"); - CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,64): error CS1637: Iterators cannot have pointer type parameters - Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p"), - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator")); //this is for putting "unsafe" on an iterator, not for the parameter type + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70), + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); //this is for putting "unsafe" on an iterator, not for the parameter type + + expectedDiagnostics = [ + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70) + ]; + + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -974,18 +3256,24 @@ public void UnsafeIteratorSignatures_PointerArray() CreateCompilation(withUnsafeOnType, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); var withUnsafeOnMembers = string.Format(template, "", "unsafe"); - CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe System.Collections.Generic.IEnumerable Iterator(int*[] p) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator").WithLocation(4, 56) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56) ); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + var withUnsafeOnTypeAndMembers = string.Format(template, "unsafe", "unsafe"); - CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe System.Collections.Generic.IEnumerable Iterator(int*[] p) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator").WithLocation(4, 56) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56) ); + + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); } [Fact] @@ -8156,10 +10444,17 @@ System.Collections.Generic.IEnumerable M() } } "; - CreateCompilation(text).VerifyDiagnostics( - // (6,22): error CS1629: Unsafe code may not appear in iterators + + var expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'S' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(S); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(S)")); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(S)").WithArguments("S").WithLocation(6, 22) + }; + + CreateCompilation(text, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -8522,10 +10817,17 @@ System.Collections.Generic.IEnumerable M() } } "; - CreateCompilation(text).VerifyDiagnostics( - // (6,17): error CS1629: Unsafe code may not appear in iterators + + var expectedDiagnostics = new[] + { + // (6,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var p = stackalloc int[1]; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "stackalloc int[1]")); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "stackalloc int[1]").WithLocation(6, 17) + }; + + CreateCompilation(text, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs index 3b9371a1c3769..610897b83d561 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs @@ -1127,12 +1127,67 @@ static async Task Main() } } }"; - var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }).VerifyDiagnostics( - // (16,22): error CS4012: Parameters or locals of type 'S1' cannot be declared in async methods or async lambda expressions. + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Dispose async").VerifyDiagnostics(); + } + + [Fact] + public void UsingPatternAsyncTest_02() + { + var source = """ + using System.Threading.Tasks; + ref struct S1 + { + public ValueTask DisposeAsync() + { + System.Console.WriteLine("Dispose async"); + return new ValueTask(Task.CompletedTask); + } + } + class C2 + { + static async Task Main() + { + await using (S1 c = new S1()) + { + await Task.Yield(); + } + } + } + """; + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); + compilation.VerifyEmitDiagnostics( + // 0.cs(14,25): error CS4007: Instance of type 'S1' cannot be preserved across 'await' or 'yield' boundary. // await using (S1 c = new S1()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "S1").WithArguments("S1").WithLocation(16, 22) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c = new S1()").WithArguments("S1").WithLocation(14, 25)); + } + [Fact] + public void UsingPatternAsyncTest_03() + { + var source = """ + using System.Threading.Tasks; + ref struct S1 + { + public S1(int x) { } + public ValueTask DisposeAsync() + { + System.Console.WriteLine("Dispose async"); + return new ValueTask(Task.CompletedTask); + } + } + class C2 + { + static async Task Main() + { + await using (S1 c = new S1(await Task.FromResult(1))) + { + } + } + } + """; + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Dispose async").VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 132f3bcfa02b7..4ebef05edb5b3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -1071,6 +1071,8 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Span_T__CopyTo_Span_T: case WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T: case WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan: + case WellKnownMember.System_Span_T__ctor_ref_T: + case WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T: // Not always available. continue; } diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 0c5b4c37ac8a3..093d3b02dcc72 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -472,6 +472,10 @@ public void WarningLevel_2() // These are the warnings introduced with the warning "wave" shipped with dotnet 8 and C# 12. Assert.Equal(8, ErrorFacts.GetWarningLevel(errorCode)); break; + case ErrorCode.WRN_BadYieldInLock: + // These are the warnings introduced with the warning "wave" shipped with dotnet 9 and C# 13. + Assert.Equal(9, ErrorFacts.GetWarningLevel(errorCode)); + break; default: // If a new warning is added, this test will fail // and whoever is adding the new warning will have to update it with the expected error level. @@ -2980,6 +2984,7 @@ public void TestIsBuildOnlyDiagnostic() case ErrorCode.ERR_InterceptableMethodMustBeOrdinary: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYield: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait: + case ErrorCode.ERR_RefLocalAcrossAwait: Assert.True(isBuildOnly, $"Check failed for ErrorCode.{errorCode}"); break; diff --git a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs index 5c47171fb8a6f..4f8554738f2b2 100644 --- a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs @@ -54,7 +54,7 @@ public enum AnalyzerTestKind /// /// Limitation 1: .NET Framework probing path. /// - /// The .NET Framework assembly loader will only call AppDomain.AssemblyResolve when it cannot satifisfy a load + /// The .NET Framework assembly loader will only call AppDomain.AssemblyResolve when it cannot satisfy a load /// request. One of the places the assembly loader will always consider when looking for dependencies of A.dll /// is the directory that A.dll was loading from (it's added to the probing path). That means if B.dll is in the /// same directory then the runtime will silently load it without a way for us to intervene. @@ -95,17 +95,19 @@ public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper, AssemblyL #if NETCOREAPP - private void Run(AnalyzerTestKind kind, Action testAction, [CallerMemberName] string? memberName = null) => + private void Run(AnalyzerTestKind kind, Action testAction, IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) => Run( kind, static (_, _) => { }, testAction, + externalResolvers, memberName); private void Run( AnalyzerTestKind kind, Action prepLoadContextAction, Action testAction, + IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) { var alc = new AssemblyLoadContext($"Test {memberName}", isCollectible: true); @@ -113,7 +115,7 @@ private void Run( { prepLoadContextAction(alc, TestFixture); var util = new InvokeUtil(); - util.Exec(TestOutputHelper, alc, TestFixture, kind, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name); + util.Exec(TestOutputHelper, alc, TestFixture, kind, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name, externalResolvers ?? []); } finally { @@ -126,6 +128,7 @@ private void Run( private void Run( AnalyzerTestKind kind, Action testAction, + IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) { AppDomain? appDomain = null; @@ -135,7 +138,7 @@ private void Run( var testOutputHelper = new AppDomainTestOutputHelper(TestOutputHelper); var type = typeof(InvokeUtil); var util = (InvokeUtil)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); - util.Exec(testOutputHelper, TestFixture, kind, testAction.Method.DeclaringType.FullName, testAction.Method.Name); + util.Exec(testOutputHelper, TestFixture, kind, testAction.Method.DeclaringType.FullName, testAction.Method.Name, externalResolvers ?? []); } finally { @@ -1421,5 +1424,115 @@ public void AssemblyLoadingInNonDefaultContext_AnalyzerReferencesSystemCollectio }); } #endif + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningNull(AnalyzerTestKind kind) + { + var resolver = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + VerifyDependencyAssemblies(loader, testFixture.Delta1); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Delta", a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningAssembly(AnalyzerTestKind kind) + { + var resolver = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + // net core assembly loader checks that the resolved assembly name is the same as the requested one + // so we use the assembly the tests are contained in as its already be loaded + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningAssembly_Or_Null(AnalyzerTestKind kind) + { + var thisAssemblyName = GetType().Assembly.GetName(); + var resolver = new TestAnalyzerAssemblyResolver(n => n == thisAssemblyName ? GetType().Assembly : null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + + loader.AddDependencyLocation(testFixture.Alpha); + Assembly alpha = loader.LoadFromPath(testFixture.Alpha); + Assert.NotNull(alpha); + + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Alpha", a.Name)), a => Assert.Equal(thisAssemblyName.Name, a.Name), a => Assert.Equal("Delta", a.Name)); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_MultipleResolvers_CanIntercept_ReturningNull(AnalyzerTestKind kind) + { + var resolver1 = new TestAnalyzerAssemblyResolver(n => null); + var resolver2 = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + VerifyDependencyAssemblies(loader, testFixture.Delta1); + + }, externalResolvers: [resolver1, resolver2]); + Assert.Collection(resolver1.CalledFor, (a => Assert.Equal("Delta", a.Name))); + Assert.Collection(resolver2.CalledFor, (a => Assert.Equal("Delta", a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_MultipleResolvers_ResolutionStops_AfterFirstResolve(AnalyzerTestKind kind) + { + var resolver1 = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); + var resolver2 = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + }, externalResolvers: [resolver1, resolver2]); + Assert.Collection(resolver1.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + Assert.Empty(resolver2.CalledFor); + } + + [Serializable] + private class TestAnalyzerAssemblyResolver(Func func) : MarshalByRefObject, IAnalyzerAssemblyResolver + { + private readonly Func _func = func; + + public List CalledFor { get; } = []; + + public Assembly? ResolveAssembly(AssemblyName assemblyName) + { + CalledFor.Add(assemblyName); + return _func(assemblyName); + } + } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs index 3dc9fec60ed45..ef1aae7ebd9d8 100644 --- a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs +++ b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs @@ -35,7 +35,7 @@ namespace Microsoft.CodeAnalysis.UnitTests public sealed class InvokeUtil { - public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compilerContext, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName) + internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compilerContext, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers) { // Ensure that the test did not load any of the test fixture assemblies into // the default load context. That should never happen. Assemblies should either @@ -48,9 +48,9 @@ public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compile using var tempRoot = new TempRoot(); AnalyzerAssemblyLoader loader = kind switch { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromDisk), - AnalyzerTestKind.LoadStream => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromStream), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(compilerContext, tempRoot.CreateDirectory().Path), + AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers.ToImmutableArray()), + AnalyzerTestKind.LoadStream => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromStream, externalResolvers.ToImmutableArray()), + AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(compilerContext, tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), _ => throw ExceptionUtilities.Unreachable() }; @@ -93,13 +93,13 @@ public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compile public sealed class InvokeUtil : MarshalByRefObject { - public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName) + internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers) { using var tempRoot = new TempRoot(); AnalyzerAssemblyLoader loader = kind switch { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(tempRoot.CreateDirectory().Path), + AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(externalResolvers.ToImmutableArray()), + AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), _ => throw ExceptionUtilities.Unreachable() }; diff --git a/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj b/src/Compilers/Core/CodeAnalysisTest/Microsoft.CodeAnalysis.UnitTests.csproj index e80335a1d132f..f3d038eafe202 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 - $(NetRoslyn);net472 + $(NetRoslynNext);net472 diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs index 5e2a1d99f08dc..c18b863c37250 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs @@ -43,15 +43,16 @@ internal partial class AnalyzerAssemblyLoader internal AssemblyLoadContext CompilerLoadContext => _compilerLoadContext; internal AnalyzerLoadOption AnalyzerLoadOption => _loadOption; - internal AnalyzerAssemblyLoader() - : this(null, AnalyzerLoadOption.LoadFromDisk) + internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) + : this(null, AnalyzerLoadOption.LoadFromDisk, externalResolvers) { } - internal AnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, AnalyzerLoadOption loadOption) + internal AnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, AnalyzerLoadOption loadOption, ImmutableArray externalResolvers) { _loadOption = loadOption; _compilerLoadContext = compilerLoadContext ?? AssemblyLoadContext.GetLoadContext(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly)!; + _externalResolvers = [.. externalResolvers, new CompilerAnalyzerAssemblyResolver(_compilerLoadContext)]; } public bool IsHostAssembly(Assembly assembly) @@ -69,7 +70,7 @@ private partial Assembly Load(AssemblyName assemblyName, string assemblyOriginal { if (!_loadContextByDirectory.TryGetValue(fullDirectoryPath, out loadContext)) { - loadContext = new DirectoryLoadContext(fullDirectoryPath, this, _compilerLoadContext); + loadContext = new DirectoryLoadContext(fullDirectoryPath, this); _loadContextByDirectory[fullDirectoryPath] = loadContext; } } @@ -107,33 +108,23 @@ internal sealed class DirectoryLoadContext : AssemblyLoadContext { internal string Directory { get; } private readonly AnalyzerAssemblyLoader _loader; - private readonly AssemblyLoadContext _compilerLoadContext; - public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader, AssemblyLoadContext compilerLoadContext) + public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader) : base(isCollectible: true) { Directory = directory; _loader = loader; - _compilerLoadContext = compilerLoadContext; } protected override Assembly? Load(AssemblyName assemblyName) { - var simpleName = assemblyName.Name!; - try + if (_loader.ResolveAssemblyExternally(assemblyName) is { } externallyResolvedAssembly) { - if (_compilerLoadContext.LoadFromAssemblyName(assemblyName) is { } compilerAssembly) - { - return compilerAssembly; - } - } - catch - { - // Expected to happen when the assembly cannot be resolved in the compiler / host - // AssemblyLoadContext. + return externallyResolvedAssembly; } // Prefer registered dependencies in the same directory first. + var simpleName = assemblyName.Name!; var assemblyPath = Path.Combine(Directory, simpleName + ".dll"); if (_loader.IsAnalyzerDependencyPath(assemblyPath)) { @@ -147,7 +138,7 @@ public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader, Ass // Note: when loading from disk the .NET runtime has a fallback step that will handle // satellite assembly loading if the call to Load(satelliteAssemblyName) fails. This // loader has a mode where it loads from Stream though and the runtime will not handle - // that automatically. Rather than bifurate our loading behavior between Disk and + // that automatically. Rather than bifurcate our loading behavior between Disk and // Stream both modes just handle satellite loading directly if (assemblyName.CultureInfo is not null && simpleName.EndsWith(".resources", StringComparison.Ordinal)) { @@ -201,6 +192,27 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) return IntPtr.Zero; } } + + /// + /// A resolver which allows a passed in from the compiler + /// to control assembly resolution. This is important because there are many exchange types + /// that need to unify across the multiple analyzer ALCs. These include common types from + /// Microsoft.CodeAnalysis.dll etc, as well as platform assemblies provided by a + /// host such as visual studio. + /// + /// + /// This resolver essentially forces any assembly that was loaded as a 'core' part of the + /// compiler to be shared across analyzers, and not loaded multiple times into each individual + /// analyzer ALC, even if the analyzer itself shipped a copy of said assembly. + /// + /// The that the core + /// compiler assemblies are already loaded into. + internal sealed class CompilerAnalyzerAssemblyResolver(AssemblyLoadContext compilerContext) : IAnalyzerAssemblyResolver + { + private readonly AssemblyLoadContext _compilerAlc = compilerContext; + + public Assembly? ResolveAssembly(AssemblyName assemblyName) => _compilerAlc.LoadFromAssemblyName(assemblyName); + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs index b4d000e335a9d..75d6271100354 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs @@ -5,6 +5,7 @@ #if !NETCOREAPP using System; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Reflection; @@ -28,8 +29,9 @@ internal partial class AnalyzerAssemblyLoader { private bool _hookedAssemblyResolve; - internal AnalyzerAssemblyLoader() + internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) { + _externalResolvers = externalResolvers; } public bool IsHostAssembly(Assembly assembly) @@ -57,6 +59,10 @@ public bool IsHostAssembly(Assembly assembly) private partial Assembly? Load(AssemblyName assemblyName, string assemblyOriginalPath) { EnsureResolvedHooked(); + if (ResolveAssemblyExternally(assemblyName) is { } externallyResolvedAssembly) + { + return externallyResolvedAssembly; + } return AppDomain.CurrentDomain.Load(assemblyName); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs index 7df1f5fb6f81c..9b35df57eee87 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs @@ -68,6 +68,14 @@ internal abstract partial class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader /// private readonly Dictionary> _knownAssemblyPathsBySimpleName = new(StringComparer.OrdinalIgnoreCase); + /// + /// A collection of s that can be used to override the assembly resolution process. + /// + /// + /// When multiple resolvers are present they are consulted in-order, with the first resolver to return a non-null + /// winning. + private readonly ImmutableArray _externalResolvers; + /// /// The implementation needs to load an with the specified . The /// parameter is the original path. It may be different than @@ -340,5 +348,33 @@ internal string GetRealAnalyzerLoadPath(string originalFullPath) .ToArray(); } } + + /// + /// Iterates the if any, to see if any of them can resolve + /// the given to an . + /// + /// The name of the assembly to resolve + /// An if one of the resolvers is successful, or + internal Assembly? ResolveAssemblyExternally(AssemblyName assemblyName) + { + if (!_externalResolvers.IsDefaultOrEmpty) + { + foreach (var resolver in _externalResolvers) + { + try + { + if (resolver.ResolveAssembly(assemblyName) is { } resolvedAssembly) + { + return resolvedAssembly; + } + } + catch + { + // Ignore if the external resolver throws + } + } + } + return null; + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs index 624768168d521..b6233d02a6d36 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs @@ -17,13 +17,19 @@ namespace Microsoft.CodeAnalysis internal sealed class DefaultAnalyzerAssemblyLoader : AnalyzerAssemblyLoader { internal DefaultAnalyzerAssemblyLoader() + : base([]) + { + } + + internal DefaultAnalyzerAssemblyLoader(ImmutableArray externalResolvers) + : base(externalResolvers) { } #if NETCOREAPP - internal DefaultAnalyzerAssemblyLoader(System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null, AnalyzerLoadOption loadOption = AnalyzerLoadOption.LoadFromDisk) - : base(compilerLoadContext, loadOption) + internal DefaultAnalyzerAssemblyLoader(System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null, AnalyzerLoadOption loadOption = AnalyzerLoadOption.LoadFromDisk, ImmutableArray? externalResolvers = null) + : base(compilerLoadContext, loadOption, externalResolvers ?? []) { } @@ -51,12 +57,12 @@ protected override string PrepareSatelliteAssemblyToLoad(string assemblyFilePath /// /// A shadow copy path will be created on Windows and this value /// will be the base directory where shadow copy assemblies are stored. - internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string windowsShadowPath) + internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string windowsShadowPath, ImmutableArray? externalResolvers = null) { #if NETCOREAPP if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return new DefaultAnalyzerAssemblyLoader(loadOption: AnalyzerLoadOption.LoadFromStream); + return new DefaultAnalyzerAssemblyLoader(loadOption: AnalyzerLoadOption.LoadFromStream, externalResolvers: externalResolvers); } #endif @@ -68,7 +74,7 @@ internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string wi throw new ArgumentException("Must be a full path.", nameof(windowsShadowPath)); } - return new ShadowCopyAnalyzerAssemblyLoader(windowsShadowPath); + return new ShadowCopyAnalyzerAssemblyLoader(windowsShadowPath, externalResolvers); } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs new file mode 100644 index 0000000000000..b6bf2b029de28 --- /dev/null +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.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 System.Reflection; + +namespace Microsoft.CodeAnalysis +{ + /// + /// Allows a host to override how assembly resolution is performed by the . + /// + internal interface IAnalyzerAssemblyResolver + { + /// + /// Attempts to resolve an assembly by name. + /// + /// The assembly to resolve + /// The resolved assembly, or + Assembly? ResolveAssembly(AssemblyName assemblyName); + } +} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs index 40592947a8ac4..9cc9ed5b29a33 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; +using System.Collections.Immutable; +using System.Reflection; #if NETCOREAPP using System.Runtime.Loader; @@ -42,15 +44,16 @@ internal sealed class ShadowCopyAnalyzerAssemblyLoader : AnalyzerAssemblyLoader internal int CopyCount => _mvidPathMap.Count; #if NETCOREAPP - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory) - : this(null, baseDirectory) + public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : this(null, baseDirectory, externalResolvers) { } - public ShadowCopyAnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, string baseDirectory) - : base(compilerLoadContext, AnalyzerLoadOption.LoadFromDisk) + public ShadowCopyAnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, string baseDirectory, ImmutableArray? externalResolvers = null) + : base(compilerLoadContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers ?? []) #else - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory) + public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : base(externalResolvers ?? []) #endif { if (baseDirectory is null) diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs index 03aebde7eba92..dfdd6d5956f06 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs @@ -588,7 +588,14 @@ void recurse(ImmutableArray members, DebugId? containingDisplay Debug.Assert(!containingDisplayClassId.HasValue); var displayClass = (INamedTypeSymbolInternal)member; - var displayClassMembers = (synthesizedMemberMap != null) ? synthesizedMemberMap[displayClass] : displayClass.GetMembers(); + + // Synthesized member map, if given, contains all the synthesized members that implement lambdas and closures. + // If not given (for PE symbols) the members are defined on the type symbol directly. + // If the display class doesn't have any synthesized members it won't be present in the map. + // See https://github.com/dotnet/roslyn/issues/73365 + var displayClassMembers = synthesizedMemberMap != null + ? (synthesizedMemberMap.TryGetValue(displayClass, out var m) ? m : []) + : displayClass.GetMembers(); if (displayClass.TypeKind == TypeKind.Struct) { diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index ba5ab7ed1b3dc..dc8bddd5e1dc0 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -325,9 +325,9 @@ public virtual void WriteTo(TextWriter writer) /// is not supported. public SourceText GetText(Encoding? encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { - var builder = new StringBuilder(this.Green.FullWidth); - this.WriteTo(new StringWriter(builder)); - return new StringBuilderText(builder, encoding, checksumAlgorithm); + var writer = SourceTextWriter.Create(encoding, checksumAlgorithm, this.Green.FullWidth); + this.WriteTo(writer); + return writer.ToSourceText(); } /// diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index dbcb737b52e09..1cdddef0d9097 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -479,6 +479,7 @@ internal enum WellKnownMember System_Span_T__ctor_Pointer, System_Span_T__ctor_Array, + System_Span_T__ctor_ref_T, System_Span_T__get_Item, System_Span_T__get_Length, System_Span_T__Slice_Int_Int, @@ -486,6 +487,7 @@ internal enum WellKnownMember System_ReadOnlySpan_T__ctor_Pointer, System_ReadOnlySpan_T__ctor_Array, System_ReadOnlySpan_T__ctor_Array_Start_Length, + System_ReadOnlySpan_T__ctor_ref_readonly_T, System_ReadOnlySpan_T__get_Item, System_ReadOnlySpan_T__get_Length, System_ReadOnlySpan_T__Slice_Int_Int, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 351d772afc467..0fd77f299f9c2 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3333,6 +3333,14 @@ static WellKnownMembers() 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Span_T__ctor_ref_T + (byte)(MemberFlags.Constructor), // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Span_T - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.ByReference, (byte)SignatureTypeCode.GenericTypeParameter, 0, // System_Span_T__get_Item (byte)(MemberFlags.PropertyGet), // Flags @@ -3388,6 +3396,14 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + // System_ReadOnlySpan_T__ctor_ref_readonly_T + (byte)(MemberFlags.Constructor), // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.ByReference, (byte)SignatureTypeCode.GenericTypeParameter, 0, + // System_ReadOnlySpan_T__get_Item (byte)(MemberFlags.PropertyGet), // Flags (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId @@ -4722,12 +4738,14 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_ObsoleteAttribute__ctor ".ctor", // System_Span_T__ctor_Pointer ".ctor", // System_Span_T__ctor_Array + ".ctor", // System_Span_T__ctor_ref_T "get_Item", // System_Span_T__get_Item "get_Length", // System_Span_T__get_Length "Slice", // System_Span_T__Slice_Int_Int ".ctor", // System_ReadOnlySpan_T__ctor_Pointer ".ctor", // System_ReadOnlySpan_T__ctor_Array ".ctor", // System_ReadOnlySpan_T__ctor_Array_Start_Length + ".ctor", // System_ReadOnlySpan_T__ctor_ref_readonly_T "get_Item", // System_ReadOnlySpan_T__get_Item "get_Length", // System_ReadOnlySpan_T__get_Length "Slice", // System_ReadOnlySpan_T__Slice_Int_Int diff --git a/src/Compilers/Test/Core/Compilation/RuntimeUtilities.cs b/src/Compilers/Test/Core/Compilation/RuntimeUtilities.cs index 57825e1362a3f..6e29d5625fc80 100644 --- a/src/Compilers/Test/Core/Compilation/RuntimeUtilities.cs +++ b/src/Compilers/Test/Core/Compilation/RuntimeUtilities.cs @@ -27,14 +27,19 @@ public static partial class RuntimeUtilities #endif internal static bool IsCoreClrRuntime => !IsDesktopRuntime; + private static int? CoreClrRuntimeVersion { get; } = IsDesktopRuntime + ? null + : typeof(object).Assembly.GetName().Version.Major; + internal static bool IsCoreClr6Runtime => IsCoreClrRuntime && RuntimeInformation.FrameworkDescription.StartsWith(".NET 6.", StringComparison.Ordinal); internal static bool IsCoreClr8OrHigherRuntime - => IsCoreClrRuntime && RuntimeInformation.FrameworkDescription.StartsWith(".NET 8.", StringComparison.Ordinal); + => CoreClrRuntimeVersion is { } v && v >= 8; internal static bool IsCoreClr9OrHigherRuntime - => IsCoreClrRuntime && RuntimeInformation.FrameworkDescription.StartsWith(".NET 9.", StringComparison.Ordinal); + => CoreClrRuntimeVersion is { } v && v >= 9; + #if NET9_0_OR_GREATER #error Make the above check be an #if NET9_OR_GREATER when we add net8 support to build #endif diff --git a/src/Compilers/Test/Core/TargetFrameworkUtil.cs b/src/Compilers/Test/Core/TargetFrameworkUtil.cs index 19a0b17c34578..592cdb613b90f 100644 --- a/src/Compilers/Test/Core/TargetFrameworkUtil.cs +++ b/src/Compilers/Test/Core/TargetFrameworkUtil.cs @@ -91,7 +91,8 @@ public enum TargetFramework Net50, Net60, Net70, - Net80 + Net80, + Net90, } /// @@ -292,6 +293,7 @@ static TargetFrameworkUtil() TargetFramework.Net60 => ImmutableArray.CreateRange(LoadDynamicReferences("Net60")), TargetFramework.NetCoreApp or TargetFramework.Net70 => ImmutableArray.CreateRange(Net70.References.All), TargetFramework.Net80 => ImmutableArray.CreateRange(LoadDynamicReferences("Net80")), + TargetFramework.Net90 => ImmutableArray.CreateRange(LoadDynamicReferences("Net90")), TargetFramework.NetFramework => NetFramework.References, TargetFramework.NetLatest => NetLatest, TargetFramework.Standard => StandardReferences, diff --git a/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb b/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb index d30639cf12746..79d119f11bf1f 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb @@ -603,7 +603,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If sourceValue > &H7000000000000000L Then Dim temporary As Double = (sourceValue - &H7000000000000000L) - If temporary < &H7000000000000000L AndAlso UncheckedCLng(temporary) > &H1000000000000000L Then + If temporary < &H7000000000000000L AndAlso UncheckedCLng(temporary) < &H1000000000000000L Then Return False End If Else diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb index d7d6b1d6070b9..25b3ddd15a495 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb @@ -605,7 +605,8 @@ End Class New TypeAndValue(uint64Type, CULng(18)), New TypeAndValue(decimalType, CDec(-11.3)), New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), - New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), + New TypeAndValue(doubleType, CDbl(&H8000000000000000L)), + New TypeAndValue(doubleType, CDbl(&H7FFFFFFFFFFFFC00L)), New TypeAndValue(typeCodeType, Int32.MinValue), New TypeAndValue(typeCodeType, Int32.MaxValue), New TypeAndValue(typeCodeType, CInt(-3)), @@ -1165,7 +1166,8 @@ End Class New TypeAndValue(uint64Type, CULng(18)), New TypeAndValue(decimalType, CDec(-11.3)), New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), - New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), + New TypeAndValue(doubleType, CDbl(&H8000000000000000L)), + New TypeAndValue(doubleType, CDbl(&H7FFFFFFFFFFFFC00L)), New TypeAndValue(typeCodeType, Int32.MinValue), New TypeAndValue(typeCodeType, Int32.MaxValue), New TypeAndValue(typeCodeType, CInt(-3)), @@ -5095,5 +5097,46 @@ End Module ]]>) End Sub + + + Public Sub ConvertLargeDoubleConstantsAndLiteralsToLong() + Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime( + + + , options:=TestOptions.ReleaseExe.WithOverflowChecks(True)) + + Dim expectedOutput = + + CompileAndVerify(compilation, expectedOutput:=expectedOutput).VerifyDiagnostics() + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 59b991849251c..46f6caf769314 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -821,7 +821,9 @@ End Namespace WellKnownMember.System_ReadOnlySpan_T__ToArray, WellKnownMember.System_Span_T__CopyTo_Span_T, WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T, - WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan + WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan, + WellKnownMember.System_Span_T__ctor_ref_T, + WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T ' Not always available. Continue For End Select @@ -1026,7 +1028,9 @@ End Namespace WellKnownMember.System_ReadOnlySpan_T__ToArray, WellKnownMember.System_Span_T__CopyTo_Span_T, WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T, - WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan + WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan, + WellKnownMember.System_Span_T__ctor_ref_T, + WellKnownMember.System_ReadOnlySpan_T__ctor_ref_readonly_T ' Not always available. Continue For End Select diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index b0246977a88b4..62c42b66d1412 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -303,7 +303,12 @@ public void Sort(IComparer comparer) } public void Sort(Comparison compare) - => Sort(Comparer.Create(compare)); + { + if (this.Count <= 1) + return; + + Sort(Comparer.Create(compare)); + } public void Sort(int startIndex, IComparer comparer) { diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 7f5a193236ab7..c3fcbfbd57629 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -105,7 +105,7 @@ protected override IList FormatBasedOnEndToken(ParsedDocument docume root, [CommonFormattingHelpers.GetFormattingSpan(root, span.Value)], options, - rules: null, + rules: default, cancellationToken).GetTextChanges(cancellationToken); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs index 33ffe9790ea5f..34d8bbbf7374d 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.InitializeParameter; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; @@ -18,6 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InitializeParameter { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] public class AddParameterCheckTests { diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InlineMethod/CSharpInlineMethodTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InlineMethod/CSharpInlineMethodTests.cs index 0246f4c4468a2..1a0cef13fbdb6 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InlineMethod/CSharpInlineMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InlineMethod/CSharpInlineMethodTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineMethod { + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsInlineMethod)] public class CSharpInlineMethodTests { diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index ef5d1071b242c..2ee4eedd0421f 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -441,7 +441,7 @@ public void M() var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var syntaxRoot = await document.GetRequiredSyntaxRootAsync(CancellationToken.None); var options = CSharpSyntaxFormattingOptions.Default; - var node = Formatter.Format(syntaxRoot, spans, workspace.Services.SolutionServices, options, rules: null, CancellationToken.None); + var node = Formatter.Format(syntaxRoot, spans, workspace.Services.SolutionServices, options, rules: default, CancellationToken.None); Assert.Equal(expected, node.ToFullString()); } diff --git a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs index b9dfcf5fcf18f..3f0061ae429f4 100644 --- a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs +++ b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs @@ -28,6 +28,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; +using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.Editor.InlineHints { @@ -266,7 +267,8 @@ bool KeepOpen() private async Task StartToolTipServiceAsync(IToolTipPresenter toolTipPresenter) { var threadingContext = _taggerProvider.ThreadingContext; - var uiList = await Task.Run(() => CreateDescriptionAsync(threadingContext.DisposalToken)).ConfigureAwait(false); + await TaskScheduler.Default; + var uiList = await CreateDescriptionAsync(threadingContext.DisposalToken).ConfigureAwait(false); await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(threadingContext.DisposalToken); toolTipPresenter.StartOrUpdate(_textView.TextSnapshot.CreateTrackingSpan(_span.Start, _span.Length, SpanTrackingMode.EdgeInclusive), uiList); diff --git a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs index 6f1533b0da943..a4e0d164dfed9 100644 --- a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs +++ b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs @@ -44,17 +44,21 @@ private VisibleBlock(double x, ImmutableArray<(double start, double end)> ySegme if (span.End.GetContainingLine().Start == span.End) return null; - // We want to draw the line right before the quote character. So -1 to get that character's position. - // Horizontally position the adornment in the center of the character. This position could actually be - // 0 if the entire doc is deleted and we haven't recomputed the updated tags yet. So be resilient for - // the position being out of bounds. + // This position could actually be 0 if the entire doc is deleted + // and we haven't recomputed the updated tags yet. So be resilient + // for the position being out of bounds. if (span.End == 0) return null; + // We want to draw the line right before the quote character. So -1 to get that character's position. + // If we position the adornment right at the end of that character it will visually merge with the + // text, so we want to back it off a little bit to the left. Half of a virtual space is gonna do the trick. + // It is important to keep this value independent of what space character is used to avoid visual bugs + // like https://github.com/dotnet/roslyn/issues/64230 var bufferPosition = span.End - 1; var anchorPointLine = view.GetTextViewLineContainingBufferPosition(bufferPosition); var bounds = anchorPointLine.GetCharacterBounds(bufferPosition); - var x = Math.Floor((bounds.Left + bounds.Right) * 0.5); + var x = Math.Floor(bounds.Right - (anchorPointLine.VirtualSpaceWidth / 2)); var firstLine = view.TextViewLines.FirstVisibleLine; var lastLine = view.TextViewLines.LastVisibleLine; diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs index a89df6cd37880..4e07cb0701ed5 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs @@ -25,6 +25,7 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -74,25 +75,27 @@ public virtual bool TryGetTelemetryId(out Guid telemetryId) return true; } - // NOTE: We want to avoid computing the operations on the UI thread. So we use Task.Run() to do this work on the background thread. - protected Task> GetOperationsAsync( + protected async Task> GetOperationsAsync( IProgress progressTracker, CancellationToken cancellationToken) { - return Task.Run( - () => CodeAction.GetOperationsAsync(this.OriginalSolution, progressTracker, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await CodeAction.GetOperationsAsync(this.OriginalSolution, progressTracker, cancellationToken).ConfigureAwait(false); } - protected Task> GetOperationsAsync( + protected async Task> GetOperationsAsync( CodeActionWithOptions actionWithOptions, object options, IProgress progressTracker, CancellationToken cancellationToken) { - return Task.Run( - () => actionWithOptions.GetOperationsAsync(this.OriginalSolution, options, progressTracker, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await actionWithOptions.GetOperationsAsync(this.OriginalSolution, options, progressTracker, cancellationToken).ConfigureAwait(false); } - protected Task> GetPreviewOperationsAsync(CancellationToken cancellationToken) + protected async Task> GetPreviewOperationsAsync(CancellationToken cancellationToken) { - return Task.Run( - () => CodeAction.GetPreviewOperationsAsync(this.OriginalSolution, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await CodeAction.GetPreviewOperationsAsync(this.OriginalSolution, cancellationToken).ConfigureAwait(false); } public void Invoke(CancellationToken cancellationToken) @@ -165,6 +168,7 @@ private async Task InvokeWorkerAsync(IProgress progressTra // Clear the progress we showed while computing the action. // We'll now show progress as we apply the action. progressTracker.Report(CodeAnalysisProgress.Clear()); + progressTracker.Report(CodeAnalysisProgress.Description(EditorFeaturesResources.Applying_changes)); using (Logger.LogBlock( FunctionId.CodeFixes_ApplyChanges, KeyValueLogMessage.Create(LogType.UserAction, m => CreateLogProperties(m)), cancellationToken)) diff --git a/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs b/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs index 95b63bb13b892..5344392f3694f 100644 --- a/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs +++ b/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs @@ -27,14 +27,21 @@ public void Report(CodeAnalysisProgress value) if (value.DescriptionValue != null) scope.Description = value.DescriptionValue; - if (value.IncompleteItemsValue != null) - Interlocked.Add(ref _totalItems, value.IncompleteItemsValue.Value); - - if (value.CompleteItemValue != null) - Interlocked.Add(ref _completedItems, value.CompleteItemValue.Value); + if (value.ClearValue) + { + Interlocked.Exchange(ref _totalItems, 0); + Interlocked.Exchange(ref _completedItems, 0); + } + else + { + if (value.IncompleteItemsValue != null) + Interlocked.Add(ref _totalItems, value.IncompleteItemsValue.Value); + + if (value.CompleteItemValue != null) + Interlocked.Add(ref _completedItems, value.CompleteItemValue.Value); + } scope.Progress.Report(new ProgressInfo(_completedItems, _totalItems)); - } } } diff --git a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs index 785c8d8e8eb64..0503a8d028d8f 100644 --- a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs +++ b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs @@ -154,7 +154,7 @@ private void ApplyEdits(Document document, ITextView textView, ITextBuffer subje var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); var formattingSpans = trackingSnapshotSpans.Select(change => CommonFormattingHelpers.GetFormattingSpan(newRoot, change.Span.ToTextSpan())); - var formattedChanges = Formatter.GetFormattedTextChanges(newRoot, formattingSpans, document.Project.Solution.Services, formattingOptions, rules: null, cancellationToken); + var formattedChanges = Formatter.GetFormattedTextChanges(newRoot, formattingSpans, document.Project.Solution.Services, formattingOptions, rules: default, cancellationToken); subjectBuffer.ApplyChanges(formattedChanges); diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs index b1f8f6233b662..a93e35b1372d2 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; @@ -24,10 +23,8 @@ using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor.Commanding; -using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GoToDefinition; @@ -106,7 +103,7 @@ public bool ExecuteCommand(TCommandArgs args, CommandExecutionContext context) if (service == null) return false; - Contract.ThrowIfNull(document); + Roslyn.Utilities.Contract.ThrowIfNull(document); // cancel any prior find-refs that might be in progress. _cancellationTokenSource.Cancel(); @@ -173,7 +170,7 @@ private async Task ExecuteCommandWorkerAsync( var cancellationToken = cancellationTokenSource.Token; var delayTask = DelayAsync(cancellationToken); - var findTask = Task.Run(() => FindResultsAsync(findContext, document, position, cancellationToken), cancellationToken); + var findTask = FindResultsAsync(findContext, document, position, cancellationToken); var firstFinishedTask = await Task.WhenAny(delayTask, findTask).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) @@ -189,7 +186,7 @@ private async Task ExecuteCommandWorkerAsync( if (definitions.Length > 0) { var title = await findContext.GetSearchTitleAsync(cancellationToken).ConfigureAwait(false); - var location = await _streamingPresenter.TryPresentLocationOrNavigateIfOneAsync( + await _streamingPresenter.TryPresentLocationOrNavigateIfOneAsync( _threadingContext, document.Project.Solution.Workspace, title ?? DisplayName, @@ -253,6 +250,9 @@ private async Task PresentResultsInStreamingPresenterAsync( private async Task FindResultsAsync( IFindUsagesContext findContext, Document document, int position, CancellationToken cancellationToken) { + // Ensure that we relinquish the thread so that the caller can proceed with their work. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + using (Logger.LogBlock(FunctionId, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken)) { await findContext.SetSearchTitleAsync(DisplayName, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs index 18a99ec18cde6..492e1b75c1b09 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs @@ -454,7 +454,7 @@ internal void ApplyConflictResolutionEdits(IInlineRenameReplacementInfo conflict // Show merge conflicts comments as unresolvable conflicts, and do not // show any other rename-related spans that overlap a merge conflict comment. mergeResult.MergeConflictCommentSpans.TryGetValue(document.Id, out var mergeConflictComments); - mergeConflictComments ??= []; + mergeConflictComments = mergeConflictComments.NullToEmpty(); foreach (var conflict in mergeConflictComments) { diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs index 0038d5fbfe21b..8eccf1e6e410c 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -19,8 +20,6 @@ using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.Threading; using Nerdbank.Streams; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -35,7 +34,9 @@ internal abstract partial class AbstractInProcLanguageClient( AbstractLanguageClientMiddleLayer? middleLayer = null) : ILanguageClient, ILanguageServerFactory, ICapabilitiesProvider, ILanguageClientCustomMessage2 { private readonly IThreadingContext _threadingContext = threadingContext; +#pragma warning disable CS0618 // Type or member is obsolete - blocked on Razor switching to new APIs for STJ - https://github.com/dotnet/roslyn/issues/73317 private readonly ILanguageClientMiddleLayer? _middleLayer = middleLayer; +#pragma warning restore CS0618 // Type or member is obsolete private readonly ILspServiceLoggerFactory _lspLoggerFactory = lspLoggerFactory; private readonly ExportProvider _exportProvider = exportProvider; @@ -200,10 +201,9 @@ internal async Task> CreateAsync> CreateAsync> CreateAsync Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, @@ -236,7 +236,7 @@ public virtual AbstractLanguageServer Create( var server = new RoslynLanguageServer( LspServiceProvider, jsonRpc, - jsonSerializer, + options, capabilitiesProvider, logger, hostServices, diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs b/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs index e7c26c5b245b0..1ecbf95264cf7 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -9,11 +9,13 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; +#pragma warning disable CS0618 // Type or member is obsolete - blocked on Razor switching to new APIs for STJ - https://github.com/dotnet/roslyn/issues/73317 internal abstract class AbstractLanguageClientMiddleLayer : ILanguageClientMiddleLayer +#pragma warning restore CS0618 // Type or member is obsolete { public abstract bool CanHandle(string methodName); public abstract Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification); public abstract Task HandleRequestAsync(string methodName, JToken methodParam, Func> sendRequest); -} \ No newline at end of file +} diff --git a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs index 6f5eb9015334d..c2515dc973c1a 100644 --- a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs +++ b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs @@ -39,47 +39,56 @@ protected AbstractDefinitionLocationService( workspace, document.Id, position, virtualSpace: 0, cancellationToken); } - public async Task GetDefinitionLocationAsync( - Document document, int position, CancellationToken cancellationToken) + public async Task GetDefinitionLocationAsync(Document document, int position, CancellationToken cancellationToken) { - var symbolService = document.GetRequiredLanguageService(); - var (controlFlowTarget, controlFlowSpan) = await symbolService.GetTargetIfControlFlowAsync( - document, position, cancellationToken).ConfigureAwait(false); - if (controlFlowTarget != null) - { - var location = await GetNavigableLocationAsync( - document, controlFlowTarget.Value, cancellationToken).ConfigureAwait(false); - return location is null ? null : new DefinitionLocation(location, new DocumentSpan(document, controlFlowSpan)); - } - else + // We want to compute this as quickly as possible so that the symbol be squiggled and navigated to. We + // don't want to wait on expensive operations like computing source-generators or skeletons if we can avoid + // it. So first try with a frozen document, then fallback to a normal document. This mirrors how go-to-def + // works as well. + return await GetDefinitionLocationWorkerAsync(document.WithFrozenPartialSemantics(cancellationToken)).ConfigureAwait(false) ?? + await GetDefinitionLocationWorkerAsync(document).ConfigureAwait(false); + + async Task GetDefinitionLocationWorkerAsync(Document document) { - // Try to compute the referenced symbol and attempt to go to definition for the symbol. - var (symbol, project, span) = await symbolService.GetSymbolProjectAndBoundSpanAsync( + var symbolService = document.GetRequiredLanguageService(); + var (controlFlowTarget, controlFlowSpan) = await symbolService.GetTargetIfControlFlowAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbol is null) - return null; - - // if the symbol only has a single source location, and we're already on it, - // try to see if there's a better symbol we could navigate to. - var remappedLocation = await GetAlternativeLocationIfAlreadyOnDefinitionAsync( - project, position, symbol, originalDocument: document, cancellationToken).ConfigureAwait(false); - if (remappedLocation != null) - return new DefinitionLocation(remappedLocation, new DocumentSpan(document, span)); - - var isThirdPartyNavigationAllowed = await IsThirdPartyNavigationAllowedAsync( - symbol, position, document, cancellationToken).ConfigureAwait(false); - - var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( - symbol, - project.Solution, - _threadingContext, - _streamingPresenter, - thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed, - cancellationToken: cancellationToken).ConfigureAwait(false); - if (location is null) - return null; - - return new DefinitionLocation(location, new DocumentSpan(document, span)); + if (controlFlowTarget != null) + { + var location = await GetNavigableLocationAsync( + document, controlFlowTarget.Value, cancellationToken).ConfigureAwait(false); + return location is null ? null : new DefinitionLocation(location, new DocumentSpan(document, controlFlowSpan)); + } + else + { + // Try to compute the referenced symbol and attempt to go to definition for the symbol. + var (symbol, project, span) = await symbolService.GetSymbolProjectAndBoundSpanAsync( + document, position, cancellationToken).ConfigureAwait(false); + if (symbol is null) + return null; + + // if the symbol only has a single source location, and we're already on it, + // try to see if there's a better symbol we could navigate to. + var remappedLocation = await GetAlternativeLocationIfAlreadyOnDefinitionAsync( + project, position, symbol, originalDocument: document, cancellationToken).ConfigureAwait(false); + if (remappedLocation != null) + return new DefinitionLocation(remappedLocation, new DocumentSpan(document, span)); + + var isThirdPartyNavigationAllowed = await IsThirdPartyNavigationAllowedAsync( + symbol, position, document, cancellationToken).ConfigureAwait(false); + + var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( + symbol, + project.Solution, + _threadingContext, + _streamingPresenter, + thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed, + cancellationToken: cancellationToken).ConfigureAwait(false); + if (location is null) + return null; + + return new DefinitionLocation(location, new DocumentSpan(document, span)); + } } } @@ -140,8 +149,7 @@ private static async Task IsThirdPartyNavigationAllowedAsync( if (containingTypeDeclaration != null) { - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - Debug.Assert(semanticModel != null); + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Allow third parties to navigate to all symbols except types/constructors // if we are navigating from the corresponding type. diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 805117ccedac8..d0b1146c9362b 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -205,45 +205,36 @@ private async ValueTask SynchronizeTextChangesAsync( async ValueTask SynchronizeTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { - // this pushes text changes to the remote side if it can. - // this is purely perf optimization. whether this pushing text change - // worked or not doesn't affect feature's functionality. + // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this + // pushing text change worked or not doesn't affect feature's functionality. // - // this basically see whether it can cheaply find out text changes - // between 2 snapshots, if it can, it will send out that text changes to - // remote side. + // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will + // send out that text changes to remote side. // - // the remote side, once got the text change, will again see whether - // it can use that text change information without any high cost and - // create new snapshot from it. + // the remote side, once got the text change, will again see whether it can use that text change information + // without any high cost and create new snapshot from it. // - // otherwise, it will do the normal behavior of getting full text from - // VS side. this optimization saves times we need to do full text - // synchronization for typing scenario. + // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves + // times we need to do full text synchronization for typing scenario. - if ((oldDocument.TryGetText(out var oldText) == false) || - (newDocument.TryGetText(out var newText) == false)) + if (!oldDocument.TryGetText(out var oldText) || + !newDocument.TryGetText(out var newText)) { // we only support case where text already exist return; } - // get text changes - var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - if (textChanges.Length == 0) - { - // no changes + // Avoid allocating text before seeing if we can bail out. + var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); + if (changeRanges.Length == 0) return; - } - // whole document case - if (textChanges.Length == 1 && textChanges[0].Span.Length == oldText.Length) - { - // no benefit here. pulling from remote host is more efficient + // no benefit here. pulling from remote host is more efficient + if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) return; - } - // only cancelled when remote host gets shutdown + var textChanges = newText.GetTextChanges(oldText).AsImmutable(); + var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) return; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs index dcc3fe3db63ed..1168c4eced621 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs @@ -196,12 +196,10 @@ private async Task CreateSolutionWithOriginalNameAsync( // Apply the original name to all linked documents to construct a consistent solution var solution = document.Project.Solution; - foreach (var documentId in document.GetLinkedDocumentIds().Add(document.Id)) - { - solution = solution.WithDocumentText(documentId, newFullText); - } + var finalSolution = solution.WithDocumentTexts( + document.GetLinkedDocumentIds().Add(document.Id).SelectAsArray(id => (id, newFullText))); - return solution; + return finalSolution; } private async Task TryGetSymbolAsync(Solution solutionWithOriginalName, DocumentId documentId, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index 4a05c589910c3..6ac112eb45bff 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -78,7 +78,7 @@ internal TestParameters( bool retainNonFixableDiagnostics = false, bool includeDiagnosticsOutsideSelection = false, string title = null, - TestHost testHost = TestHost.InProcess, + TestHost testHost = TestHost.OutOfProcess, string workspaceKind = null, bool includeNonLocalDocumentDiagnostics = false, bool treatPositionIndicatorsAsCode = false) @@ -414,7 +414,7 @@ internal Task TestInRegularAndScriptAsync( object fixProviderData = null, ParseOptions parseOptions = null, string title = null, - TestHost testHost = TestHost.InProcess) + TestHost testHost = TestHost.OutOfProcess) { return TestInRegularAndScript1Async( initialMarkup, expectedMarkup, @@ -454,7 +454,7 @@ internal Task TestAsync( OptionsCollectionAlias globalOptions = null, object fixProviderData = null, CodeActionPriority? priority = null, - TestHost testHost = TestHost.InProcess) + TestHost testHost = TestHost.OutOfProcess) { return TestAsync( initialMarkup, diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs index 6027738ffdb05..9d1bfe0e37ead 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs @@ -152,7 +152,7 @@ internal override async Task> GetDiagnosticsAsync( EditorTestWorkspace workspace, TestParameters parameters) { var (analyzer, _) = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); var document = GetDocumentAndSelectSpan(workspace, out var span); var allDiagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(workspace, document, span, includeNonLocalDocumentDiagnostics: parameters.includeNonLocalDocumentDiagnostics); @@ -164,7 +164,7 @@ internal override async Task> GetDiagnosticsAsync( EditorTestWorkspace workspace, TestParameters parameters) { var (analyzer, fixer) = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index 6128a5ceb1d2e..97da08493caea 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Remote.Testing; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -100,26 +101,23 @@ protected override async Task> GetDiagnosticsWorkerAs internal override Task GetCodeRefactoringAsync(EditorTestWorkspace workspace, TestParameters parameters) => throw new NotImplementedException("No refactoring provided in diagnostic test"); - protected static void AddAnalyzerToWorkspace(Workspace workspace, DiagnosticAnalyzer analyzer, TestParameters parameters) + protected static void AddAnalyzerToWorkspace(Workspace workspace, DiagnosticAnalyzer analyzer) { - AnalyzerReference[] analyzeReferences; + AnalyzerReference[] analyzerReferences; if (analyzer != null) { - Contract.ThrowIfTrue(parameters.testHost == TestHost.OutOfProcess, $"Out-of-proc testing is not supported since {analyzer} can't be serialized."); - - analyzeReferences = new[] { new AnalyzerImageReference(ImmutableArray.Create(analyzer)) }; + var analyzerImageReference = new AnalyzerImageReference(ImmutableArray.Create(analyzer)); + analyzerReferences = [analyzerImageReference]; } else { // create a serializable analyzer reference: - analyzeReferences = new[] - { + analyzerReferences = [ new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile), - new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile) - }; + new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile)]; } - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(analyzeReferences)); + workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(analyzerReferences)); } protected static Document GetDocumentAndSelectSpan(EditorTestWorkspace workspace, out TextSpan span) diff --git a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs index 9d9b0d488ef3b..47974f2cfb369 100644 --- a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs +++ b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs @@ -2,94 +2,113 @@ // 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.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.UnitTests.LinkedFiles +namespace Microsoft.CodeAnalysis.Editor.UnitTests.LinkedFiles; + +public sealed class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest { - public partial class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest - { - private const string WorkspaceXml = @" - - - - - - - "; - - protected internal override string GetLanguage() - => LanguageNames.CSharp; - - protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters) - => new TestCodeRefactoringProvider(); - - [WpfFact] - public async Task TestCodeActionPreviewAndApply() + private const string WorkspaceXml = """ + + + + + + + + + """; + + private const string s_expectedCode = """ + internal class C { - // TODO: WPF required due to https://github.com/dotnet/roslyn/issues/46153 - using var workspace = EditorTestWorkspace.Create(WorkspaceXml, composition: EditorTestCompositions.EditorFeaturesWpf); - var codeIssueOrRefactoring = await GetCodeRefactoringAsync(workspace, new TestParameters()); + private class D + { + } + } + """; - var expectedCode = "private class D { }"; + protected internal override string GetLanguage() + => LanguageNames.CSharp; - await TestActionOnLinkedFiles( - workspace, - expectedText: expectedCode, - action: codeIssueOrRefactoring.CodeActions[0].action, - expectedPreviewContents: expectedCode); - } + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters) + => new TestCodeRefactoringProvider(); - [Fact] - public async Task TestWorkspaceTryApplyChangesDirectCall() - { - using var workspace = EditorTestWorkspace.Create(WorkspaceXml); - var solution = workspace.CurrentSolution; + [WpfFact] + public async Task TestCodeActionPreviewAndApply() + { + // TODO: WPF required due to https://github.com/dotnet/roslyn/issues/46153 + using var workspace = EditorTestWorkspace.Create(WorkspaceXml, composition: EditorTestCompositions.EditorFeaturesWpf); + var codeIssueOrRefactoring = await GetCodeRefactoringAsync(workspace, new TestParameters()); + + await TestActionOnLinkedFiles( + workspace, + expectedText: s_expectedCode, + action: codeIssueOrRefactoring.CodeActions[0].action, + expectedPreviewContents: """ + internal class C + { + private class D + { + ... + """); + } - var documentId = workspace.Documents.Single(d => !d.IsLinkFile).Id; - var text = await workspace.CurrentSolution.GetDocument(documentId).GetTextAsync(); + [Fact] + public async Task TestWorkspaceTryApplyChangesDirectCall() + { + using var workspace = EditorTestWorkspace.Create(WorkspaceXml); + var solution = workspace.CurrentSolution; - var linkedDocumentId = workspace.Documents.Single(d => d.IsLinkFile).Id; - var linkedText = await workspace.CurrentSolution.GetDocument(linkedDocumentId).GetTextAsync(); + var documentId = workspace.Documents.Single(d => !d.IsLinkFile).Id; + var text = await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync(); - var newSolution = solution - .WithDocumentText(documentId, text.Replace(13, 1, "D")) - .WithDocumentText(linkedDocumentId, linkedText.Replace(0, 6, "private")); + var linkedDocumentId = workspace.Documents.Single(d => d.IsLinkFile).Id; + var linkedText = await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync(); - workspace.TryApplyChanges(newSolution); + var textString = linkedText.ToString(); - var expectedMergedText = "private class D { }"; - Assert.Equal(expectedMergedText, (await workspace.CurrentSolution.GetDocument(documentId).GetTextAsync()).ToString()); - Assert.Equal(expectedMergedText, (await workspace.CurrentSolution.GetDocument(linkedDocumentId).GetTextAsync()).ToString()); - } + var newSolution = solution + .WithDocumentText(documentId, text.Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocumentId, linkedText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); + + workspace.TryApplyChanges(newSolution); - protected override ParseOptions GetScriptOptions() - => throw new NotSupportedException(); + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync()).ToString()); + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync()).ToString()); + } + + protected override ParseOptions GetScriptOptions() + => throw new NotSupportedException(); - private class TestCodeRefactoringProvider : CodeRefactorings.CodeRefactoringProvider + private sealed class TestCodeRefactoringProvider : CodeRefactoringProvider + { + public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { - public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) - { - var document = context.Document; - var linkedDocument = document.Project.Solution.Projects.Single(p => p != document.Project).Documents.Single(); + var document = context.Document; + var linkedDocument = document.Project.Solution.Projects.Single(p => p != document.Project).Documents.Single(); + var sourceText = await linkedDocument.GetTextAsync(); + var textString = sourceText.ToString(); - var newSolution = document.Project.Solution - .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(13, 1, "D")) - .WithDocumentText(linkedDocument.Id, (await linkedDocument.GetTextAsync()).Replace(0, 6, "private")); + var newSolution = document.Project.Solution + .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocument.Id, sourceText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); -#pragma warning disable RS0005 - context.RegisterRefactoring(CodeAction.Create("Description", (ct) => Task.FromResult(newSolution)), context.Span); -#pragma warning restore RS0005 - } + context.RegisterRefactoring(CodeAction.Create("Description", _ => Task.FromResult(newSolution)), context.Span); } } } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index ab0b2ed0739c0..44cc65b75a962 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -204,14 +204,14 @@ private protected async Task AssertFormatAsync(string expected, string code, IEn ? formattingService.GetFormattingOptions(options, fallbackOptions: null) : formattingService.DefaultOptions; - var rules = formattingRuleProvider.CreateRule(documentSyntax, 0).Concat(Formatter.GetDefaultFormattingRules(document)); + ImmutableArray rules = [formattingRuleProvider.CreateRule(documentSyntax, 0), .. Formatter.GetDefaultFormattingRules(document)]; AssertFormat(workspace, expected, formattingOptions, rules, clonedBuffer, documentSyntax.Root, spans); // format with node and transform AssertFormatWithTransformation(workspace, expected, formattingOptions, rules, documentSyntax.Root, spans); } - internal void AssertFormatWithTransformation(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, SyntaxNode root, IEnumerable spans) + internal void AssertFormatWithTransformation(Workspace workspace, string expected, SyntaxFormattingOptions options, ImmutableArray rules, SyntaxNode root, IEnumerable spans) { var newRootNode = Formatter.Format(root, spans, workspace.Services.SolutionServices, options, rules, CancellationToken.None); @@ -224,7 +224,7 @@ internal void AssertFormatWithTransformation(Workspace workspace, string expecte Assert.True(newRootNodeFromString.IsEquivalentTo(newRootNode)); } - internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) + internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattingOptions options, ImmutableArray rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) { var result = Formatter.GetFormattedTextChanges(root, spans, workspace.Services.SolutionServices, options, rules, CancellationToken.None); var actual = ApplyResultAndGetFormattedText(clonedBuffer, result); diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs index a817940d49017..8998fcec76edd 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Options; +using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; namespace Roslyn.Test.Utilities @@ -22,10 +23,12 @@ internal readonly record struct InitializationOptions() internal LSP.ClientCapabilities ClientCapabilities { get; init; } = new LSP.ClientCapabilities(); internal WellKnownLspServerKinds ServerKind { get; init; } = WellKnownLspServerKinds.AlwaysActiveVSLspServer; internal Action? OptionUpdater { get; init; } = null; + internal bool CallInitialize { get; init; } = true; internal bool CallInitialized { get; init; } = true; internal object? ClientTarget { get; init; } = null; internal string? Locale { get; init; } = null; internal IEnumerable? AdditionalAnalyzers { get; init; } = null; + internal IJsonRpcMessageFormatter? ClientMessageFormatter { get; init; } = null; } } } diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index b4b32bd2383e1..1fa9da38fb528 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -7,6 +7,8 @@ using System.Collections.Immutable; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -26,13 +28,11 @@ 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.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; using Xunit; @@ -44,6 +44,8 @@ namespace Roslyn.Test.Utilities [UseExportProvider] public abstract partial class AbstractLanguageServerProtocolTests { + private static readonly SystemTextJsonFormatter s_messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); + private protected readonly AbstractLspLogger TestOutputLspLogger; protected AbstractLanguageServerProtocolTests(ITestOutputHelper? testOutputHelper) { @@ -124,8 +126,8 @@ private protected static LSP.ClientCapabilities GetCapabilities(bool isVS) /// the actual object to be converted to JSON. public static void AssertJsonEquals(T1 expected, T2 actual) { - var expectedStr = JsonConvert.SerializeObject(expected); - var actualStr = JsonConvert.SerializeObject(actual); + var expectedStr = JsonSerializer.Serialize(expected, s_messageFormatter.JsonSerializerOptions); + var actualStr = JsonSerializer.Serialize(actual, s_messageFormatter.JsonSerializerOptions); AssertEqualIgnoringWhitespace(expectedStr, actualStr); } @@ -269,7 +271,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( SortText = sortText, InsertTextFormat = LSP.InsertTextFormat.Plaintext, Kind = kind, - Data = JObject.FromObject(new CompletionResolveData(resultId, ProtocolConversions.DocumentToTextDocumentIdentifier(document))), + Data = JsonSerializer.SerializeToElement(new CompletionResolveData(resultId, ProtocolConversions.DocumentToTextDocumentIdentifier(document)), s_messageFormatter.JsonSerializerOptions), Preselect = preselect, VsResolveTextEditOnCommit = vsResolveTextEditOnCommit, LabelDetails = labelDetails @@ -512,13 +514,6 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U } }; - internal static JsonMessageFormatter CreateJsonMessageFormatter() - { - var messageFormatter = new JsonMessageFormatter(); - LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(messageFormatter.JsonSerializer); - return messageFormatter; - } - internal sealed class TestLspServer : IAsyncDisposable { public readonly EditorTestWorkspace TestWorkspace; @@ -536,7 +531,8 @@ private TestLspServer( LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream, - object? clientTarget = null) + object? clientTarget = null, + IJsonRpcMessageFormatter? clientMessageFormatter = null) { TestWorkspace = testWorkspace; ClientCapabilities = clientCapabilities; @@ -545,7 +541,9 @@ private TestLspServer( LanguageServer = target; - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, CreateJsonMessageFormatter()), clientTarget) + clientMessageFormatter ??= RoslynLanguageServer.CreateJsonMessageFormatter(); + + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, clientMessageFormatter), clientTarget) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; @@ -571,13 +569,16 @@ internal static async Task CreateAsync(EditorTestWorkspace testWo var (clientStream, serverStream) = FullDuplexStream.CreatePair(); var languageServer = CreateLanguageServer(serverStream, serverStream, testWorkspace, initializationOptions.ServerKind, logger); - var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, languageServer, clientStream, initializationOptions.ClientTarget); + var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, languageServer, clientStream, initializationOptions.ClientTarget, initializationOptions.ClientMessageFormatter); - await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + if (initializationOptions.CallInitialize) { - Capabilities = initializationOptions.ClientCapabilities, - Locale = initializationOptions.Locale, - }, CancellationToken.None); + await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + { + Capabilities = initializationOptions.ClientCapabilities, + Locale = initializationOptions.Locale, + }, CancellationToken.None); + } if (initializationOptions.CallInitialized) { @@ -607,13 +608,13 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var factory = workspace.ExportProvider.GetExportedValue(); - var jsonMessageFormatter = CreateJsonMessageFormatter(); + var jsonMessageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; - var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializer, capabilitiesProvider, serverKind, logger, workspace.Services.HostServices); + var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializerOptions, capabilitiesProvider, serverKind, logger, workspace.Services.HostServices); jsonRpc.StartListening(); return languageServer; diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 5d857106ba245..6fa92b52053b2 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -166,7 +166,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit oldTree As SyntaxTree, newDirtySpan As SnapshotSpan, newTree As SyntaxTree, - cancellationToken As CancellationToken) As IEnumerable(Of AbstractFormattingRule) + cancellationToken As CancellationToken) As ImmutableArray(Of AbstractFormattingRule) ' if the span we are going to format is same as the span that got changed, don't bother to do anything special. ' just do full format of the span. @@ -212,12 +212,17 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit ' for now, do very simple checking. basically, we see whether we get same number of indent operation for the give span. alternative, but little bit ' more expensive and complex, we can actually calculate indentation right after the span, and see whether that is changed. not sure whether that much granularity ' is needed. + Dim coreRules = Formatter.GetDefaultFormattingRules(languageServices) + If GetNumberOfIndentOperations(languageServices, options, oldTree, oldDirtySpan, cancellationToken) = GetNumberOfIndentOperations(languageServices, options, newTree, newDirtySpan, cancellationToken) Then - Return (New NoAnchorFormatterRule()).Concat(Formatter.GetDefaultFormattingRules(languageServices)) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(coreRules.Length + 1) + result.Add(New NoAnchorFormatterRule()) + result.AddRange(coreRules) + Return result.MoveToImmutable() End If - Return Formatter.GetDefaultFormattingRules(languageServices) + Return coreRules End Function Private Shared Function GetNumberOfIndentOperations( diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineMethod/VisualBasicInlineMethodTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineMethod/VisualBasicInlineMethodTests.vb index 035d4cdb4b5a6..7edcc4255ead6 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineMethod/VisualBasicInlineMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineMethod/VisualBasicInlineMethodTests.vb @@ -7,9 +7,10 @@ Imports Microsoft.CodeAnalysis.Testing Imports Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InlineMethod - + Public Class VisualBasicInlineMethodTests + Private Class TestVerifier Inherits VisualBasicCodeRefactoringVerifier(Of VisualBasicInlineMethodRefactoringProvider).Test Private Const Marker As String = "##" @@ -795,8 +796,8 @@ Imports System Public Class TestClass Public Sub Caller(i As Integer, j As Integer) Dim x = Function() - Return i * j - End Function() + Return i * j + End Function() End Sub ## Private Function Callee(i As Integer, j As Integer) as Func(Of Integer) @@ -1287,7 +1288,7 @@ Public Class TestClass End Class", " Public Class TestClass Public Sub Caller(i As Integer) - Dim y = If (true, 10, 100) + Dim y = If(true, 10, 100) End Sub ## Private Function Callee(a As Boolean) As Integer diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index 3b9aceb35410f..be91476713e7e 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.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.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.UnitTests @@ -66,7 +67,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting workspace.Documents.First(Function(d) d.SelectedSpans.Any()).SelectedSpans, workspace.Services.SolutionServices, options, - rules, + rules.ToImmutableArray(), CancellationToken.None) AssertResult(expected, clonedBuffer, changes) diff --git a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs index 98ab13f19a3f9..f513c3af27576 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -265,11 +266,11 @@ public override void AddAlignTokensOperations(List list, S } } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { base.AddSuppressOperations(list, node, in nextOperation); - // not sure exactly what is happening here, but removing the bellow causesthe indentation to be wrong. + // not sure exactly what is happening here, but removing the bellow causes the indentation to be wrong. // remove suppression rules for array and collection initializer if (node.IsInitializerForArrayOrCollectionCreationExpression()) diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index e12e26279379c..d22b7ece85de4 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -888,8 +888,8 @@ public override async Task> DetermineCascadedSymbolsFrom return convertedMethodGroups; } - protected override IEnumerable GetFormattingRules(Document document) - => Formatter.GetDefaultFormattingRules(document).Concat(new ChangeSignatureFormattingRule()); + protected override ImmutableArray GetFormattingRules(Document document) + => [.. Formatter.GetDefaultFormattingRules(document), new ChangeSignatureFormattingRule()]; protected override TArgumentSyntax AddNameToArgument(TArgumentSyntax newArgument, string name) { diff --git a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs index ae26da1577ddb..03b08066988bc 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable; @@ -67,14 +68,21 @@ private static async Task EnableNullableReferenceTypesAsync( Project project, CodeActionPurpose purpose, CodeActionOptionsProvider fallbackOptions, IProgress _, CancellationToken cancellationToken) { var solution = project.Solution; - foreach (var document in project.Documents) - { - if (await document.IsGeneratedCodeAsync(cancellationToken).ConfigureAwait(false)) - continue; + var updatedDocumentRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: project.Documents, + produceItems: static async (document, callback, fallbackOptions, cancellationToken) => + { + if (await document.IsGeneratedCodeAsync(cancellationToken).ConfigureAwait(false)) + return; - var updatedDocumentRoot = await EnableNullableReferenceTypesAsync(document, fallbackOptions, cancellationToken).ConfigureAwait(false); - solution = solution.WithDocumentSyntaxRoot(document.Id, updatedDocumentRoot); - } + var updatedDocumentRoot = await EnableNullableReferenceTypesAsync( + document, fallbackOptions, cancellationToken).ConfigureAwait(false); + callback((document.Id, updatedDocumentRoot)); + }, + args: fallbackOptions, + cancellationToken).ConfigureAwait(false); + + solution = solution.WithDocumentSyntaxRoots(updatedDocumentRoots); if (purpose is CodeActionPurpose.Apply) { diff --git a/src/Features/CSharp/Portable/Debugging/DataTipInfoGetter.cs b/src/Features/CSharp/Portable/Debugging/DataTipInfoGetter.cs index c6068148d8f59..86b1ba4a94e65 100644 --- a/src/Features/CSharp/Portable/Debugging/DataTipInfoGetter.cs +++ b/src/Features/CSharp/Portable/Debugging/DataTipInfoGetter.cs @@ -37,7 +37,7 @@ internal static async Task GetInfoAsync(Document document, int : default; } - if (expression.IsAnyLiteralExpression()) + if (expression is LiteralExpressionSyntax) { // If the user hovers over a literal, give them a DataTip for the type of the // literal they're hovering over. diff --git a/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs b/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs index 6b4a2e9bd14db..9cdedae3a1579 100644 --- a/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs +++ b/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs @@ -63,7 +63,7 @@ public static async Task FormatDocumentAsync(Document document, Syntax document, [node.FullSpan], options, - CSharpDecompiledSourceFormattingRule.Instance.Concat(Formatter.GetDefaultFormattingRules(document)), + [CSharpDecompiledSourceFormattingRule.Instance, .. Formatter.GetDefaultFormattingRules(document)], cancellationToken).ConfigureAwait(false); return formattedDoc; diff --git a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs index 8a81b6d09d4b1..6bd117bf73410 100644 --- a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs +++ b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs @@ -59,7 +59,8 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageServer; "CS9178", // ErrorCode.ERR_InterceptorCannotBeGeneric "CS9207", // ErrorCode.ERR_InterceptableMethodMustBeOrdinary "CS8419", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYield - "CS8420" // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait + "CS8420", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait + "CS9217" // ErrorCode.ERR_RefLocalAcrossAwait )] [Shared] internal sealed class CSharpLspBuildOnlyDiagnostics : ILspBuildOnlyDiagnostics diff --git a/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs b/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs index f9c34dedd824e..d52d8ba83cd72 100644 --- a/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs +++ b/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs @@ -203,6 +203,6 @@ protected static string MakeUnique(string baseName, INamedTypeSymbol containingT return NameGenerator.GenerateUniqueName(baseName, containingTypeMemberNames.ToSet(), StringComparer.Ordinal); } - internal override IEnumerable GetConstructorNodes(INamedTypeSymbol containingType) + protected override IEnumerable GetConstructorNodes(INamedTypeSymbol containingType) => containingType.Constructors.SelectMany(c => c.DeclaringSyntaxReferences.Select(d => d.GetSyntax())); } diff --git a/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs b/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs index 15c8118bed05b..af747c6470b09 100644 --- a/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs +++ b/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs @@ -6,6 +6,8 @@ using System.Composition; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GoToDefinition; @@ -16,16 +18,12 @@ namespace Microsoft.CodeAnalysis.CSharp.GoToDefinition; [ExportLanguageService(typeof(IGoToDefinitionSymbolService), LanguageNames.CSharp), Shared] -internal class CSharpGoToDefinitionSymbolService : AbstractGoToDefinitionSymbolService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpGoToDefinitionSymbolService() : AbstractGoToDefinitionSymbolService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpGoToDefinitionSymbolService() - { - } - - protected override ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation) - => symbol; + protected override Task FindRelatedExplicitlyDeclaredSymbolAsync(Project project, ISymbol symbol, CancellationToken cancellationToken) + => Task.FromResult(symbol); protected override int? GetTargetPositionIfControlFlow(SemanticModel semanticModel, SyntaxToken token) { diff --git a/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs index 3daf3339608c8..83eb0d70ac6b4 100644 --- a/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InvertIf; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -21,15 +22,11 @@ namespace Microsoft.CodeAnalysis.CSharp.InvertIf; using static SyntaxFactory; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InvertIf), Shared] -internal sealed class CSharpInvertIfCodeRefactoringProvider : AbstractInvertIfCodeRefactoringProvider< +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpInvertIfCodeRefactoringProvider() : AbstractInvertIfCodeRefactoringProvider< SyntaxKind, StatementSyntax, IfStatementSyntax, StatementSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpInvertIfCodeRefactoringProvider() - { - } - protected override string GetTitle() => CSharpFeaturesResources.Invert_if; diff --git a/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs b/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs index 3f51710433897..e8681c83af413 100644 --- a/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs +++ b/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs @@ -57,8 +57,8 @@ protected override async Task AddAssemblyInfoRegionAsync(Document docu return document.WithSyntaxRoot(newRoot); } - protected override IEnumerable GetFormattingRules(Document document) - => s_memberSeparationRule.Concat(Formatter.GetDefaultFormattingRules(document)); + protected override ImmutableArray GetFormattingRules(Document document) + => [s_memberSeparationRule, .. Formatter.GetDefaultFormattingRules(document)]; protected override async Task ConvertDocCommentsToRegularCommentsAsync(Document document, IDocumentationCommentFormattingService docCommentFormattingService, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs index c96dbb0c61400..adf0adae32d83 100644 --- a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -84,13 +85,8 @@ protected override async Task UpdatePropertyAsync( return updatedProperty.WithTrailingTrivia(trailingTrivia).WithAdditionalAnnotations(SpecializedFormattingAnnotation); } - protected override IEnumerable GetFormattingRules(Document document) - { - var rules = new List { new SingleLinePropertyFormattingRule() }; - rules.AddRange(Formatter.GetDefaultFormattingRules(document)); - - return rules; - } + protected override ImmutableArray GetFormattingRules(Document document) + => [new SingleLinePropertyFormattingRule(), .. Formatter.GetDefaultFormattingRules(document)]; private class SingleLinePropertyFormattingRule : AbstractFormattingRule { diff --git a/src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs b/src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs index 0c82ed1fef90a..a42150ea0aa99 100644 --- a/src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs +++ b/src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs @@ -4,15 +4,17 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Xunit; -using VerifyCS = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.CSharpCodeRefactoringVerifier< - Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay.CSharpAddDebuggerDisplayCodeRefactoringProvider>; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddDebuggerDisplay { + using VerifyCS = CSharpCodeRefactoringVerifier; + + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsAddDebuggerDisplay)] public sealed class AddDebuggerDisplayTests { diff --git a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs index a6e95f5bbfa59..5c095d186aa4a 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertConversionOperat { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.ConvertCast)] public class ConvertDirectCastToTryCastTests { diff --git a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs index 7a124319b2737..c2747d36481e2 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertConversionOperat { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.ConvertCast)] public class ConvertTryCastToDirectCastTests { diff --git a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index ad6bd6680ee2b..ff958c2b38960 100644 --- a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -16,6 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.ConvertIfTo { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsConvertIfToSwitch)] public class ConvertIfToSwitchTests { diff --git a/src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs b/src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs index 5edb251a13646..959a63de1a199 100644 --- a/src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs +++ b/src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs @@ -4156,10 +4156,10 @@ List M(IEnumerable nums) return /*30*/ /* 1 *//* 2 *//* 3 *//* 4 */// 5 /*31*//* 6 */ (from/* 8 *//* 7 *//* 9 */x /* 10 */ in/* 11 */nums/* 12 */// 13 - /* 14 */// 15 - /* 16 *//* 17 */ - let y /* 18 */ = /* 19 */ x + 1/* 20 *///21 - select y)/* 24 *//*27*///28 + /* 14 */// 15 + /* 16 *//* 17 */ + let y /* 18 */ = /* 19 */ x + 1/* 20 *///21 + select y)/* 24 *//*27*///28 .ToList()/* 22 *//* 23 *//* 25 *///26 ; //32 } @@ -4205,7 +4205,7 @@ List M(IEnumerable nums) /*25*//* 14 */// 15 /* 6 */ (from/* 8 *//* 7 *//* 9 */x /* 10 */ in/* 11 */nums/* 12 */// 13 - select x + 1)/* 18 *//*21*///22 + select x + 1)/* 18 *//*21*///22 .ToList()/* 16 *//* 17 *//* 19 *///20 ; //26 } @@ -4223,7 +4223,8 @@ List M(IEnumerable nums) { /*23*/ return /*24*/ /* 1 *//* 2 *//* 3 *//* 4 */// 5 - /*25*/nums /* 12 */.Select( + /*25*/ + nums /* 12 */.Select( /* 6 *//* 7 *//* 14 */// 15 /* 9 */x /* 10 */ => x + 1/* 18 *//*21*///22 /* 8 *//* 11 */// 13 @@ -4268,7 +4269,7 @@ int M(IEnumerable nums) /*23*//* 14 */// 15 /* 6 */ (from/* 8 *//* 7 *//* 9 */x /* 10 */ in/* 11 */nums/* 12 */// 13 - select x)/* 10 *//*19*///20 + select x)/* 10 *//*19*///20 .Count()/* 16 *//* 17 *///18 ; //24 } @@ -4286,7 +4287,8 @@ int M(IEnumerable nums) { /*21*/ return /*22*/ /* 1 *//* 2 *//* 3 *//* 4 */// 5 - /*23*/nums /* 12 *//* 6 *//* 7 *//* 14 */// 15 + /*23*/ + nums /* 12 *//* 6 *//* 7 *//* 14 */// 15 /* 9 *//* 10 *//* 10 *//*19*///20 /* 8 *//* 11 */// 13 .Count()/* 16 *//* 17 *///18 @@ -4328,11 +4330,11 @@ void M(IEnumerable nums) { foreach (var (a /* 12 */ , b /*16*/ ) in /* 1 */from/* 2 */int /* 3 */ n1 /* 4 */in/* 5 */nums/* 6 */// 7 - /* 8*/// 9 - /* 10 *//* 11 */ - let a /* 12 */ = /* 13 */ n1 + n1/* 14*//* 15 */ - let b /*16*/ = /*17*/ n1 * n1/*18*///19 - select (a /* 12 */ , b /*16*/ )/*22*//*23*/) + /* 8*/// 9 + /* 10 *//* 11 */ + let a /* 12 */ = /* 13 */ n1 + n1/* 14*//* 15 */ + let b /*16*/ = /*17*/ n1 * n1/*18*///19 + select (a /* 12 */ , b /*16*/ )/*22*//*23*/) { /*20*/ Console.WriteLine(a + b);//21 @@ -4384,7 +4386,7 @@ void M(IEnumerable nums) /* 10 */ where/* 11 *//* 12 */n1 /* 13 */ > /* 14 */ 0/* 15 */// 16 select n1/* 4 *//* 21 */// 22 - /*23*//*24*/ + /*23*//*24*/ ) { /*19*/ diff --git a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 63276e4ab6594..b7c16c95881c4 100644 --- a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ConvertNamespace; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; using Xunit; @@ -17,6 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] public class ConvertNamespaceRefactoringTests { public static IEnumerable EndOfDocumentSequences diff --git a/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs b/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs index 5afc6c7b2a6d6..9f5265d6f9a0d 100644 --- a/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs +++ b/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.ConvertPrimaryToRegularConstructor; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -13,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertPrimaryToRegular using VerifyCS = CSharpCodeRefactoringVerifier; +[UseExportProvider] public class ConvertPrimaryToRegularConstructorTests { private const string FieldNamesCamelCaseWithFieldUnderscorePrefixEditorConfig = """ diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs index e2a42e80b6614..1fdac5e56aa39 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ConvertProgram; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -15,6 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertProgram { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] public class ConvertToProgramMainRefactoringTests { [Fact] diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs index 53ce410193fc7..9b03d72ae6249 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ConvertProgram; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -15,6 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertProgram { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] public class ConvertToTopLevelStatementsRefactoringTests { [Fact] diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs index 6a59132c0fd36..cfad68025c998 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToInterpolatedSt using VerifyCS = CSharpCodeRefactoringVerifier; +[UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)] public class ConvertConcatenationToInterpolatedStringTests { diff --git a/src/Features/CSharpTest/ConvertToRawString/ConvertInterpolatedStringToRawStringTests.cs b/src/Features/CSharpTest/ConvertToRawString/ConvertInterpolatedStringToRawStringTests.cs index 3ce45a521362c..e674d034b3c94 100644 --- a/src/Features/CSharpTest/ConvertToRawString/ConvertInterpolatedStringToRawStringTests.cs +++ b/src/Features/CSharpTest/ConvertToRawString/ConvertInterpolatedStringToRawStringTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToRawString; using VerifyCS = CSharpCodeRefactoringVerifier< ConvertStringToRawStringCodeRefactoringProvider>; +[UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsConvertToRawString)] public class ConvertInterpolatedStringToRawStringTests { diff --git a/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs b/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs index 974bf8623616d..771a5585b224f 100644 --- a/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs +++ b/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToRawString using VerifyCS = CSharpCodeRefactoringVerifier< ConvertStringToRawStringCodeRefactoringProvider>; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsConvertToRawString)] public class ConvertRegularStringToRawStringTests { diff --git a/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs b/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs index 2937f37c13f73..96848f438dc7f 100644 --- a/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs +++ b/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs @@ -10,12 +10,10 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ConvertTupleToStruct; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.NamingStyles; using Microsoft.CodeAnalysis.Remote.Testing; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; @@ -47,7 +45,7 @@ private static async Task TestAsync( string? equivalenceKey = null, LanguageVersion languageVersion = LanguageVersion.CSharp9, OptionsCollection? options = null, - TestHost testHost = TestHost.InProcess, + TestHost testHost = TestHost.OutOfProcess, string[]? actions = null) { if (index != 0) diff --git a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs index 83d05ebe6d16f..e0a01c8c2e449 100644 --- a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs +++ b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs @@ -445,8 +445,8 @@ void Method() var parameters = new TestParameters(); using var workspace = CreateWorkspaceFromOptions(source, parameters); - var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create(new CSharpCompilerDiagnosticAnalyzer())); - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); + var analyzerReference = new AnalyzerImageReference([new CSharpCompilerDiagnosticAnalyzer()]); + workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); var incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace); diff --git a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs index 55ab94f4c6afa..b01e956ea20eb 100644 --- a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs +++ b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs @@ -9,15 +9,19 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Utilities; using Xunit; -using VerifyCS = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.CSharpCodeRefactoringVerifier< - Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider>; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.EnableNullable { + using VerifyCS = CSharpCodeRefactoringVerifier; + + [UseExportProvider] public class EnableNullableTests { private static readonly Func s_enableNullableInFixedSolution = diff --git a/src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs b/src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs index 852c2887faf45..f53fc904d6e85 100644 --- a/src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs +++ b/src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs @@ -22,6 +22,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.GenerateDefaultConstruc using VerifyRefactoring = CSharpCodeRefactoringVerifier< GenerateDefaultConstructorsCodeRefactoringProvider>; + [UseExportProvider] public class GenerateDefaultConstructorsTests { private static async Task TestRefactoringAsync(string source, string fixedSource, int index = 0) diff --git a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs index c2bce6842af06..163e0e096f175 100644 --- a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs +++ b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.GenerateFromMembers.Add { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsAddConstructorParametersFromMembers)] public class AddConstructorParametersFromMembersTests { diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index d23dbb7d5117b..12a86d1e86b75 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests; @@ -16,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.InlineTemporary { [Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)] - public class InlineTemporaryTests : AbstractCSharpCodeActionTest_NoEditor + public sealed class InlineTemporaryTests : AbstractCSharpCodeActionTest_NoEditor { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpInlineTemporaryCodeRefactoringProvider(); @@ -253,16 +252,23 @@ public void M() [Fact] public async Task Conversion_NoConversion() { - await TestFixOneAsync( + await TestAsync( """ - { int [||]x = 3; + class C + { + void F(){ int [||]x = 3; x.ToString(); } + } + """, + """ + class C + { + void F(){ + 3.ToString(); } + } """, - """ - { - 3.ToString(); } - """); + CSharpParseOptions.Default); } [Fact] @@ -690,7 +696,7 @@ class Program static void Main() { int x = 2; - Bar(x < x, x > 1+2); + Bar(x < x, x > 1 + 2); } static void Bar(object a, object b) @@ -5739,7 +5745,7 @@ public class Goo { public void Bar() { - var target = new List<object;gt>(); + var target = new List<object>(); var [||]newItems = new List<Goo>(); target.AddRange(newItems); } @@ -5763,7 +5769,7 @@ public class Goo { public void Bar() { - var target = new List<object;gt>(); + var target = new List<object>(); target.AddRange(new List<Goo>()); } } diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 82cf8a801c445..dd30dd77f6304 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -5977,7 +5977,7 @@ void M() { int x = 1; int {|Rename:y1|} = C.y; - var t = new { y= y1, y= y1 }; // this is an error already + var t = new { y = y1, y = y1 }; // this is an error already } } """; diff --git a/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs index 856f322b68f74..b69f6484d85c0 100644 --- a/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs +++ b/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs @@ -3,177 +3,90 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf; + +public partial class InvertIfTests { - [Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] - public partial class InvertIfTests + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause1() { - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause1() - { - await TestInRegularAndScriptAsync( - """ - class C + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) - { - [||]if (c) - { - return 1; - } - } - return 2; - } - } - } - """, - """ - class C - { - void M() - { - switch (o) - { - case 1: - if (c) - { - [||]if (!c) - { - } - else - { - return 1; - } - } - return 2; - } - } - } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause2() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - switch (o) - { - case 1: + case 1: + if (c) + { [||]if (c) { - f(); + return 1; } - g(); - g(); - break; - } + } + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: + case 1: + if (c) + { [||]if (!c) { } else { - f(); + return 1; } - g(); - g(); - break; - } - } - } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause3() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - [||]if (c) - { - f(); - } - g(); - g(); - } - } - """, - """ - class C - { - void M() - { - if (!c) - { - } - else - { - f(); - } - g(); - g(); + } + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause4() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause2() + { + await TestAsync(""" + class C + { + void M() { - bool M() + switch (o) { - if (c) - { + case 1: [||]if (c) { f(); } g(); - } - return false; + g(); + break; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - bool M() + switch (o) { - if (c) - { - if (!c) + case 1: + [||]if (!c) { } else @@ -181,36 +94,74 @@ bool M() f(); } g(); - } - return false; + g(); + break; + } + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause3() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) + { + f(); + } + g(); + g(); + } + } + """, """ + class C + { + void M() + { + if (!c) + { + } + else + { + f(); } + g(); + g(); } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause5() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause4() + { + await TestAsync(""" + class C + { + bool M() { - void M() + if (c) { [||]if (c) { f(); } - - g(); g(); } + return false; } - """, - """ - class C + } + """, """ + class C + { + bool M() { - void M() + if (c) { if (!c) { @@ -219,435 +170,487 @@ void M() { f(); } - - g(); g(); } + return false; + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause5() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) + { + f(); + } + + g(); + g(); } - """); - } + } + """, """ + class C + { + void M() + { + if (!c) + { + } + else + { + f(); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause6() - { - await TestInRegularAndScriptAsync( - """ - class C + g(); + g(); + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause6() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (c) + case 1: + [||]if (c) + { + if (c) { - if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (!c) - { - } - else + case 1: + [||]if (!c) + { + } + else + { + if (c) { - if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause7() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause7() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) + case 1: + if (c) + { + [||]if (c) { - [||]if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) + case 1: + if (c) + { + if (!c) + { + } + else { - if (!c) - { - } - else - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40909")] - public async Task IfWithoutElse_MoveIfBodyToElseClause8() - { - await TestInRegularAndScriptAsync( - """ - using System.Diagnostics; - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40909")] + public async Task IfWithoutElse_MoveIfBodyToElseClause8() + { + await TestAsync(""" + using System.Diagnostics; + class C + { + private static bool IsFalse(bool val) { - private static bool IsFalse(bool val) { + [|if|] (!val) { - [|if|] (!val) - { - return true; - } - Debug.Assert(val); + return true; } - return false; + Debug.Assert(val); } + return false; } - """, - """ - using System.Diagnostics; - class C + } + """, """ + using System.Diagnostics; + class C + { + private static bool IsFalse(bool val) { - private static bool IsFalse(bool val) { + if (val) { - if (val) - { - } - else - { - return true; - } - Debug.Assert(val); } - return false; + else + { + return true; + } + Debug.Assert(val); } + return false; } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (!c) + { + continue; + } + // comments + f(); + } + } + } + """, """ + class C + { + void M() + { + foreach (var item in list) + { + [||]if (c) { - [||]if (!c) - { - continue; - } // comments f(); } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody2() + { + await TestAsync(""" + class C + { + void M() { - void M() + while (c) { - foreach (var item in list) + if (c) { [||]if (c) { - // comments - f(); + continue; } + if (c()) + return; } } } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody2() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + while (c) { - while (c) + if (c) { - if (c) + [||]if (!c) { - [||]if (c) - { - continue; - } if (c()) return; } } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody3() + { + await TestAsync(""" + class C + { + void M() { - void M() + while (c) { - while (c) { - if (c) + [||]if (c) { - [||]if (!c) - { - if (c()) - return; - } + continue; } + if (c()) + return; } } } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody3() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + while (c) { - while (c) { + [||]if (!c) { - [||]if (c) - { - continue; - } if (c()) return; } } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - while (c) - { - { - [||]if (!c) - { - if (c()) - return; - } - } - } + [||]if (c) + break; + return; } } - """); - } - - [Fact] - public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements1() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (c) - break; + [||]if (!c) return; - } + break; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements2() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (!c) { - [||]if (!c) - return; - break; + return; } + break; } } - """); - } - - [Fact] - public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements2() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (c) { - [||]if (!c) - { - return; - } break; } + return; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_WithElseClause1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (c) - { - break; - } + [||]if (!c) return; - } + f(); } } - """); - } - - [Fact] - public async Task IfWithoutElse_WithElseClause1() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (!c) - return; + if (c) f(); - } + else + return; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_WithNegatedCondition1() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) { } + } + } + """, """ + class C + { + void M() { - void M() - { - foreach (var item in list) - { - if (c) - f(); - else - return; - } - } + if (!c) { } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNegatedCondition1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - [||]if (c) { } + [||]if (c) + { + f(); + } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - if (!c) { } + [||]if (!c) + { + continue; + } + f(); } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement2() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) { [||]if (c) { @@ -656,13 +659,14 @@ void M() } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) { [||]if (!c) { @@ -672,252 +676,205 @@ void M() } } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement2() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement3() + { + await TestAsync(""" + class C + { + void M() { - void M() + [||]if (c) { - foreach (var item in list) - { - { - [||]if (c) - { - f(); - } - } - } + f(); } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + [||]if (!c) { - foreach (var item in list) - { - { - [||]if (!c) - { - continue; - } - f(); - } - } + return; } + f(); } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement3() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement4() + { + await TestAsync(""" + class C + { + void M() { - void M() + for (;;) { [||]if (c) { - f(); + break; } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + for (;;) { [||]if (!c) { - return; + continue; } - f(); + break; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement4() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithSubsequentExitPointStatement1() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - for (;;) - { + case 1: [||]if (c) { - break; + f(); + f(); } - } + break; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - for (;;) - { + case 1: [||]if (!c) { - continue; + break; } + f(); + f(); break; - } } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithSubsequentExitPointStatement1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithSubsequentExitPointStatement2() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (c) + case 1: + [||]if (c) + { + if (c) { - f(); - f(); + return 1; } - break; - } + } + + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (!c) - { - break; - } - f(); - f(); - break; - } + case 1: + [||]if (!c) + { + return 2; + } + if (c) + { + return 1; + } + + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithSubsequentExitPointStatement2() - { - await TestInRegularAndScriptAsync( - """ - class C + [Theory] + [InlineData("get")] + [InlineData("set")] + [InlineData("init")] + public async Task IfWithoutElse_InPropertyAccessors(string accessor) + { + await TestAsync($$""" + class C + { + private bool _b; + + public string Prop { - void M() + {{accessor}} { - switch (o) + [||]if (_b) { - case 1: - [||]if (c) - { - if (c) - { - return 1; - } - } - - return 2; + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); } } } - """, - """ - class C + } + """, $$""" + class C + { + private bool _b; + + public string Prop { - void M() + {{accessor}} { - switch (o) + if (!_b) { - case 1: - [||]if (!c) - { - return 2; - } - if (c) - { - return 1; - } - - return 2; + return; } + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); } } - """); - } - - [Theory] - [InlineData("get")] - [InlineData("set")] - [InlineData("init")] - public async Task IfWithoutElse_InPropertyAccessors(string accessor) - { - await TestInRegularAndScriptAsync( -$@"class C -{{ - private bool _b; - - public string Prop - {{ - {accessor} - {{ - [||]if (_b) - {{ - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(); - }} - }} - }} -}}", -$@"class C -{{ - private bool _b; - - public string Prop - {{ - {accessor} - {{ - if (!_b) - {{ - return; - }} - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(); - }} - }} -}}"); - } + } + """); } } diff --git a/src/Features/CSharpTest/InvertIf/InvertIfTests.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.cs index 1fec5e17cbf1b..9dee33f7b2f7e 100644 --- a/src/Features/CSharpTest/InvertIf/InvertIfTests.cs +++ b/src/Features/CSharpTest/InvertIf/InvertIfTests.cs @@ -3,33 +3,28 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.InvertIf; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf; + +[UseExportProvider, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] +public partial class InvertIfTests { - [Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] - public partial class InvertIfTests : AbstractCSharpCodeActionTest_NoEditor + private static Task TestInsideMethodAsync( + string initial, + string expected) { - private async Task TestFixOneAsync( - string initial, - string expected) - { - await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected)); - } - - protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) - => new CSharpInvertIfCodeRefactoringProvider(); + return TestAsync(CreateTreeText(initial), CreateTreeText(expected)); - private static string CreateTreeText(string initial) + static string CreateTreeText(string initial) { - return - """ + return $$""" class A { bool a = true; @@ -39,428 +34,499 @@ class A void Goo() { - """ + initial + """ + {{initial}} } } """; } + } - [Fact] - public async Task TestSingleLine_Identifier() + private static async Task TestAsync(string initial, string expected, LanguageVersion languageVersion = LanguageVersion.Latest) + { + await new CSharpCodeRefactoringVerifier.Test { - await TestFixOneAsync( + TestCode = initial, + FixedCode = expected, + LanguageVersion = languageVersion, + CompilerDiagnostics = CompilerDiagnostics.None, + }.RunAsync(); + } + + [Fact] + public async Task TestSingleLine_Identifier() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else { b(); }", @"if (!a) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_IdentifierWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IdentifierWithTrivia() + { + await TestInsideMethodAsync( @"[||]if /*0*/(/*1*/a/*2*/)/*3*/ { a(); } else { b(); }", @"if /*0*/(/*1*/!a/*2*/)/*3*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotIdentifier() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotIdentifier() + { + await TestInsideMethodAsync( @"[||]if (!a) { a(); } else { b(); }", @"if (a) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotIdentifierWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotIdentifierWithTrivia() + { + await TestInsideMethodAsync( @"[||]if /*0*/(/*1*/!/*1b*/a/*2*/)/*3*/ { a(); } else { b(); }", @"if /*0*/(/*1*/a/*2*/)/*3*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_EqualsEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_EqualsEquals() + { + await TestInsideMethodAsync( @"[||]if (a == b) { a(); } else { b(); }", @"if (a != b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotEquals() + { + await TestInsideMethodAsync( @"[||]if (a != b) { a(); } else { b(); }", @"if (a == b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_GreaterThan() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_GreaterThan() + { + await TestInsideMethodAsync( @"[||]if (a > b) { a(); } else { b(); }", @"if (a <= b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_GreaterThanEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_GreaterThanEquals() + { + await TestInsideMethodAsync( @"[||]if (a >= b) { a(); } else { b(); }", @"if (a < b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_LessThan() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_LessThan() + { + await TestInsideMethodAsync( @"[||]if (a < b) { a(); } else { b(); }", @"if (a >= b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_LessThanEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_LessThanEquals() + { + await TestInsideMethodAsync( @"[||]if (a <= b) { a(); } else { b(); }", @"if (a > b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParentheses() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParentheses() + { + await TestInsideMethodAsync( @"[||]if ((a)) { a(); } else { b(); }", @"if (!a) { b(); } else { a(); }"); - } + } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26427")] - public async Task TestSingleLine_DoubleParenthesesWithInnerTrivia() - { - await TestFixOneAsync( + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26427")] + public async Task TestSingleLine_DoubleParenthesesWithInnerTrivia() + { + await TestInsideMethodAsync( @"[||]if ((/*1*/a/*2*/)) { a(); } else { b(); }", @"if (/*1*/!a/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParenthesesWithMiddleTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParenthesesWithMiddleTrivia() + { + await TestInsideMethodAsync( @"[||]if (/*1*/(a)/*2*/) { a(); } else { b(); }", @"if (/*1*/!a/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParenthesesWithOutsideTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParenthesesWithOutsideTrivia() + { + await TestInsideMethodAsync( @"[||]if /*before*/((a))/*after*/ { a(); } else { b(); }", @"if /*before*/(!a)/*after*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Is() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Is() + { + await TestInsideMethodAsync( @"[||]if (a is Goo) { a(); } else { b(); }", @"if (a is not Goo) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_MethodCall() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_MethodCall() + { + await TestInsideMethodAsync( @"[||]if (a.Goo()) { a(); } else { b(); }", @"if (!a.Goo()) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or() + { + await TestInsideMethodAsync( @"[||]if (a || b) { a(); } else { b(); }", @"if (!a && !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or2() + { + await TestInsideMethodAsync( @"[||]if (!a || !b) { a(); } else { b(); }", @"if (a && b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or3() + { + await TestInsideMethodAsync( @"[||]if (!a || b) { a(); } else { b(); }", @"if (a && !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or4() + { + await TestInsideMethodAsync( @"[||]if (a | b) { a(); } else { b(); }", @"if (!a & !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And() + { + await TestInsideMethodAsync( @"[||]if (a && b) { a(); } else { b(); }", @"if (!a || !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And2() + { + await TestInsideMethodAsync( @"[||]if (!a && !b) { a(); } else { b(); }", @"if (a || b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And3() + { + await TestInsideMethodAsync( @"[||]if (!a && b) { a(); } else { b(); }", @"if (a || !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And4() + { + await TestInsideMethodAsync( @"[||]if (a & b) { a(); } else { b(); }", @"if (!a | !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_ParenthesizeAndForPrecedence() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_ParenthesizeAndForPrecedence() + { + await TestInsideMethodAsync( @"[||]if (a && b || c) { a(); } else { b(); }", @"if ((!a || !b) && !c) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Plus() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Plus() + { + await TestInsideMethodAsync( @"[||]if (a + b) { a(); } else { b(); }", @"if (!(a + b)) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_True() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_True() + { + await TestInsideMethodAsync( @"[||]if (true) { a(); } else { b(); }", @"if (false) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_TrueWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_TrueWithTrivia() + { + await TestInsideMethodAsync( @"[||]if (/*1*/true/*2*/) { a(); } else { b(); }", @"if (/*1*/false/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_False() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_False() + { + await TestInsideMethodAsync( @"[||]if (false) { a(); } else { b(); }", @"if (true) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_OtherLiteralExpression() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_OtherLiteralExpression() + { + await TestInsideMethodAsync( @"[||]if (literalexpression) { a(); } else { b(); }", @"if (!literalexpression) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_TrueAndFalse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_TrueAndFalse() + { + await TestInsideMethodAsync( @"[||]if (true && false) { a(); } else { b(); }", @"if (false || true) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NoCurlyBraces() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NoCurlyBraces() + { + await TestInsideMethodAsync( @"[||]if (a) a(); else b();", @"if (!a) b(); else a();"); - } + } - [Fact] - public async Task TestSingleLine_CurlyBracesOnIf() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CurlyBracesOnIf() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else b();", @"if (!a) b(); else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_CurlyBracesOnElse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CurlyBracesOnElse() + { + await TestInsideMethodAsync( @"[||]if (a) a(); else { b(); }", @"if (!a) { b(); } else a();"); - } + } - [Fact] - public async Task TestSingleLine_IfElseIf() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IfElseIf() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else if (b) { b(); }", @"if (!a) { if (b) { b(); } } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_IfElseIfElse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IfElseIfElse() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else if (b) { b(); } else { c(); }", @"if (!a) { if (b) { b(); } else { c(); } } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_CompoundConditional() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CompoundConditional() + { + await TestInsideMethodAsync( @"[||]if (((a == b) && (c != d)) || ((e < f) && (!g))) { a(); } else { b(); }", @"if ((a != b || c == d) && (e >= f || g)) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Trivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Trivia() + { + await TestInsideMethodAsync( @"[||]if /*1*/ (a) /*2*/ { /*3*/ a() /*4*/; /*5*/ } /*6*/ else if /*7*/ (b) /*8*/ { /*9*/ b(); /*10*/ } /*11*/ else /*12*/ { /*13*/ c(); /*14*/} /*15*/", @"if /*1*/ (!a) /*2*/ { if /*7*/ (b) /*8*/ { /*9*/ b(); /*10*/ } /*11*/ else /*12*/ { /*13*/ c(); /*14*/} /*6*/ } else { /*3*/ a() /*4*/; /*5*/ } /*15*/"); - } + } - [Fact] - public async Task TestKeepTriviaWithinExpression_BrokenCode() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestKeepTriviaWithinExpression_BrokenCode() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [||]if (a || + b && + c < // comment + d) { - [||]if (a || - b && - c < // comment - d) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!a && + (!b || + c >= // comment + d)) { - if (!a && - (!b || - c >= // comment - d)) - { - b(); - } - else - { - a(); - } + b(); + } + else + { + a(); } } - """); - } + } + """); + } - [Fact] - public async Task TestKeepTriviaWithinExpression() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestKeepTriviaWithinExpression() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + bool a = true; + bool b = true; + bool c = true; + bool d = true; + + [||]if (a || + b && + c < // comment + d) { - bool a = true; - bool b = true; - bool c = true; - bool d = true; - - [||]if (a || - b && - c < // comment - d) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + bool a = true; + bool b = true; + bool c = true; + bool d = true; + + if (!a && + (!b || + c >= // comment + d)) + { + b(); + } + else { - bool a = true; - bool b = true; - bool c = true; - bool d = true; + a(); + } + } + } + """); + } - if (!a && - (!b || - c >= // comment - d)) + [Fact] + public async Task TestMultiline_IfElseIfElse() + { + await TestAsync(""" + class A + { + void Goo() + { + [||]if (a) + { + a(); + } + else if (b) + { + b(); + } + else + { + c(); + } + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { b(); } else { - a(); + c(); } } + else + { + a(); + } } - """); - } + } + """); + } - [Fact] - public async Task TestMultiline_IfElseIfElse() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultiline_IfElseIfElseSelection1() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [|if (a) { - [||]if (a) - { - a(); - } - else if (b) + a(); + } + else if (b) + { + b(); + } + else + { + c(); + }|] + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { b(); } @@ -469,245 +535,195 @@ void Goo() c(); } } + else + { + a(); + } } - """, - """ - class A + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultiline_IfElseIfElseSelection2() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [|if (a) + { + a(); + }|] + else if (b) { - if (!a) + b(); + } + else + { + c(); + } + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { - if (b) - { - b(); - } - else - { - c(); - } + b(); } else { - a(); + c(); } } + else + { + a(); + } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultiline_IfElseIfElseSelection1() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultilineMissing_IfElseIfElseSubSelection() + { + var code = """ + class A + { + void Goo() { - void Goo() + if (a) { - [|if (a) - { - a(); - } - else if (b) - { - b(); - } - else - { - c(); - }|] + a(); } - } - """, - """ - class A - { - void Goo() + [|else if (b) { - if (!a) - { - if (b) - { - b(); - } - else - { - c(); - } - } - else - { - a(); - } + b(); } + else + { + c(); + }|] } - """); - } + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultiline_IfElseIfElseSelection2() - { - await TestInRegularAndScriptAsync( - """ - class A + await TestAsync(code, code); + } + + [Fact] + public async Task TestMultiline_IfElse() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() - { - [|if (a) - { - a(); - }|] - else if (b) - { - b(); - } - else - { - c(); - } - } + [||]if (foo) + bar(); + else + if (baz) + Quux(); } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!foo) { - if (!a) - { - if (b) - { - b(); - } - else - { - c(); - } - } - else - { - a(); - } + if (baz) + Quux(); } + else + bar(); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultilineMissing_IfElseIfElseSubSelection() - { - await TestMissingInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestMultiline_OpenCloseBracesSameLine() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() - { - if (a) - { - a(); - } - [|else if (b) - { - b(); - } - else - { - c(); - }|] + [||]if (foo) { + x(); + x(); + } else { + y(); + y(); } } - """); - } - - [Fact] - public async Task TestMultiline_IfElse() - { - await TestInRegularAndScriptAsync( - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!foo) { - [||]if (foo) - bar(); - else - if (baz) - Quux(); + y(); + y(); } - } - """, - """ - class A - { - void Goo() + else { - if (!foo) - { - if (baz) - Quux(); - } - else - bar(); + x(); + x(); } } - """); - } + } + """); + } - [Fact] - public async Task TestMultiline_OpenCloseBracesSameLine() - { - await TestInRegularAndScriptAsync( - """ - class A - { - void Goo() - { - [||]if (foo) { - x(); - x(); - } else { - y(); - y(); - } - } + [Fact] + public async Task TestMultiline_Trivia() + { + await TestAsync(""" + class A + { + void Goo() + { /*1*/ + [||]if (a) /*2*/ + { /*3*/ + /*4*/ + goo(); /*5*/ + /*6*/ + } /*7*/ + else if (b) /*8*/ + { /*9*/ + /*10*/ + goo(); /*11*/ + /*12*/ + } /*13*/ + else /*14*/ + { /*15*/ + /*16*/ + goo(); /*17*/ + /*18*/ + } /*19*/ + /*20*/ } - """, - """ - class A - { - void Goo() + } + """, """ + class A + { + void Goo() + { /*1*/ + if (!a) /*2*/ { - if (!foo) - { - y(); - y(); - } - else - { - x(); - x(); - } - } - } - """); - } - [Fact] - public async Task TestMultiline_Trivia() - { - await TestInRegularAndScriptAsync( - """ - class A - { - void Goo() - { /*1*/ - [||]if (a) /*2*/ - { /*3*/ - /*4*/ - goo(); /*5*/ - /*6*/ - } /*7*/ - else if (b) /*8*/ + if (b) /*8*/ { /*9*/ /*10*/ goo(); /*11*/ @@ -719,963 +735,907 @@ void Goo() goo(); /*17*/ /*18*/ } /*19*/ - /*20*/ } + else + { /*3*/ + /*4*/ + goo(); /*5*/ + /*6*/ + } /*7*/ + /*20*/ } - """, - """ - class A - { - void Goo() - { /*1*/ - if (!a) /*2*/ - { - if (b) /*8*/ - { /*9*/ - /*10*/ - goo(); /*11*/ - /*12*/ - } /*13*/ - else /*14*/ - { /*15*/ - /*16*/ - goo(); /*17*/ - /*18*/ - } /*19*/ - } - else - { /*3*/ - /*4*/ - goo(); /*5*/ - /*6*/ - } /*7*/ - /*20*/ - } - } - """); - } + } + """); + } - [Fact] - public async Task TestOverlapsHiddenPosition1() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task TestOverlapsHiddenPosition1() + { + var code = """ + class C + { + void F() { - void F() + #line hidden + [||]if (a) { - #line hidden - [||]if (a) - { - a(); - } - else - { - b(); - } - #line default + a(); + } + else + { + b(); } + #line default } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition2() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition2() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - #line hidden - a(); - #line default - } - else - { - b(); - } + #line hidden + a(); + #line default + } + else + { + b(); } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition3() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition3() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - a(); - } - else - { - #line hidden - b(); - #line default - } + a(); + } + else + { + #line hidden + b(); + #line default } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition4() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition4() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - #line hidden - a(); - } - else - { - b(); - #line default - } + #line hidden + a(); + } + else + { + b(); + #line default } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition5() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition5() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - a(); - #line hidden - } - else - { - #line default - b(); - } + a(); + #line hidden + } + else + { + #line default + b(); } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition6() - { - await TestInRegularAndScriptAsync( - """ - #line hidden - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition6() + { + await TestAsync(""" + #line hidden + class C + { + void F() { - void F() + #line default + [||]if (a) { - #line default - [||]if (a) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - - """ - #line hidden - class C + } + """, """ + #line hidden + class C + { + void F() { - void F() + #line default + if (!a) { - #line default - if (!a) - { - b(); - } - else - { - a(); - } + b(); + } + else + { + a(); } } - """); - } + } + """); - [Fact] - public async Task TestOverlapsHiddenPosition7() - { - await TestInRegularAndScriptAsync( - """ - #line hidden - class C + } + + [Fact] + public async Task TestOverlapsHiddenPosition7() + { + await TestAsync(""" + #line hidden + class C + { + void F() { - void F() + #line default + [||]if (a) { - #line default - [||]if (a) - { - a(); - } - else - { - b(); - } - #line hidden + a(); + } + else + { + b(); } + #line hidden } - #line default - """, - - """ - #line hidden - class C + } + #line default + """, """ + #line hidden + class C + { + void F() { - void F() + #line default + if (!a) { - #line default - if (!a) - { - b(); - } - else - { - a(); - } - #line hidden + b(); + } + else + { + a(); } + #line hidden } - #line default - """); - } + } + #line default + """); + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero2() + { + await TestInsideMethodAsync( @"string[] x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string[] x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero3() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0x0) { a(); } else { b(); } } } ", @"string x; if (x.Length == 0x0) { b(); } else { a(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero4() + { + await TestInsideMethodAsync( @"string x; [||]if (0 < x.Length) { a(); } else { b(); } } } ", @"string x; if (0 == x.Length) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero1() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero1() + { + await TestInsideMethodAsync( @"byte x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"byte x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero2() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero2() + { + await TestInsideMethodAsync( @"ushort x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"ushort x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero3() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero3() + { + await TestInsideMethodAsync( @"uint x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"uint x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero4() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero4() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (x > 0) { a(); } else { b(); } } } ", @"ulong x = 1; if (x == 0) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToNotEqualsZero1() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToNotEqualsZero1() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (0 == x) { a(); } else { b(); } } } ", @"ulong x = 1; if (0 != x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToNotEqualsZero2() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToNotEqualsZero2() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (x == 0) { a(); } else { b(); } } } ", @"ulong x = 1; if (x != 0) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530505")] - public async Task TestSingleLine_SimplifyLongLengthEqualsZero() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530505")] + public async Task TestSingleLine_SimplifyLongLengthEqualsZero() + { + await TestInsideMethodAsync( @"string[] x; [||]if (x.LongLength > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string[] x; if (x.LongLength == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length >= 0) { a(); } else { b(); } } } ", @"string x; if (x.Length < 0) { b(); } else { a(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero2() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0.0f) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string x; if (x.Length <= 0.0f) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29434")] - public async Task TestIsExpression() - { - await TestInRegularAndScriptAsync( + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29434")] + public async Task TestIsExpression() + { + await TestAsync( @"class C { void M(object o) { [||]if (o is C) { a(); } else { } } }", @"class C { void M(object o) { if (o is not C) { } else { a(); } } }"); - } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestEmptyIf() - { - await TestInRegularAndScriptAsync( - @"class C { void M(string s){ [||]if (s == ""a""){}else{ s = ""b""}}}", - @"class C { void M(string s){ if (s != ""a""){ s = ""b""}}}"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestEmptyIf() + { + await TestAsync( + @"class C { void M(string s){ [||]if (s == ""a""){}else{ s = ""b""}}}", + @"class C { void M(string s){ if (s != ""a"") { s = ""b""}}}"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestOnlySingleLineCommentIf() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestOnlySingleLineCommentIf() + { + await TestAsync(""" + class C + { + void M(string s) { - void M(string s) + [||]if (s == "a") { - [||]if (s == "a") - { - // A single line comment - } - else - { - s = "b" - } + // A single line comment + } + else + { + s = "b" } } - """, - """ - class C + } + """, """ + class C + { + void M(string s) { - void M(string s) + if (s != "a") { - if (s != "a") - { - s = "b" - } - else - { - // A single line comment - } + s = "b" + } + else + { + // A single line comment } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestOnlyMultilineLineCommentIf() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M(string s) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestOnlyMultilineLineCommentIf() + { + await TestAsync(""" + class C + { + void M(string s) + { + [||]if (s == "a") { - [||]if (s == "a") - { - /* - * This is - * a multiline - * comment with - * two words - * per line. - */ - } - else - { - s = "b" - } + /* + * This is + * a multiline + * comment with + * two words + * per line. + */ + } + else + { + s = "b" } } - """, - """ - class C - { - void M(string s) + } + """, """ + class C + { + void M(string s) + { + if (s != "a") { - if (s != "a") - { - s = "b" - } - else - { - /* - * This is - * a multiline - * comment with - * two words - * per line. - */ - } + s = "b" + } + else + { + /* + * This is + * a multiline + * comment with + * two words + * per line. + */ } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp6() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp6() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (!(c is object)) { - if (!(c is object)) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)); - } + } + """, LanguageVersion.CSharp6); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp8() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp8() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is null) { - if (c is null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8)); - } + } + """, LanguageVersion.CSharp8); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp9() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp9() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is null) { - if (c is null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9)); - } + } + """, LanguageVersion.CSharp9); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsNotObjectCheck_CSharp8() - { - // Not terrific. But the starting code is not legal C#8 either. In this case because we don't even support - // 'not' patterns wee dont' bother diving into the pattern to negate it, and we instead just negate the - // expression. - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsNotObjectCheck_CSharp8() + { + // Not terrific. But the starting code is not legal C#8 either. In this case because we don't even support + // 'not' patterns wee don't bother diving into the pattern to negate it, and we instead just negate the + // expression. + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is not object) { - [||]if (c is not object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is object) { - if (c is object) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8)); - } + } + """, LanguageVersion.CSharp8); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsNotObjectCheck_CSharp9() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsNotObjectCheck_CSharp9() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is not object) { - [||]if (c is not object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is not null) { - if (c is not null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9)); - } + } + """, LanguageVersion.CSharp9); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_GreaterThan() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThan() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p > 10) { - [||]if (p > 10) - { - System.Console.WriteLine("p is not null and p.Value > 10"); - } + System.Console.WriteLine("p is not null and p.Value > 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p > 10)) { - if (!(p > 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value > 10"); + return; } + System.Console.WriteLine("p is not null and p.Value > 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_GreaterThanOrEqual() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThanOrEqual() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p >= 10) { - [||]if (p >= 10) - { - System.Console.WriteLine("p is not null and p.Value >= 10"); - } + System.Console.WriteLine("p is not null and p.Value >= 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p >= 10)) { - if (!(p >= 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value >= 10"); + return; } + System.Console.WriteLine("p is not null and p.Value >= 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_LessThan() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThan() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p < 10) { - [||]if (p < 10) - { - System.Console.WriteLine("p is not null and p.Value < 10"); - } + System.Console.WriteLine("p is not null and p.Value < 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p < 10)) { - if (!(p < 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value < 10"); + return; } + System.Console.WriteLine("p is not null and p.Value < 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_LessThanOrEqual() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThanOrEqual() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p <= 10) { - [||]if (p <= 10) - { - System.Console.WriteLine("p is not null and p.Value <= 10"); - } + System.Console.WriteLine("p is not null and p.Value <= 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p <= 10)) { - if (!(p <= 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value <= 10"); + return; } + System.Console.WriteLine("p is not null and p.Value <= 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestNullableReference_GreaterThan() - { - await TestInRegularAndScriptAsync( - """ - #nullable enable - using System; - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestNullableReference_GreaterThan() + { + await TestAsync(""" + #nullable enable + using System; + class C + { + void M(C? p) { - void M(C? p) + [||]if (p > new C()) { - [||]if (p > new C()) - { - Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); - } + 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 + + 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) { - void M(C? p) + if (p <= new C()) { - if (p <= new C()) - { - return; - } - Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); + return; } - - 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(); + Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); } - """); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40585")] - public async Task TestYieldBreak() - { - await TestInRegularAndScriptAsync( - """ - using System.Collections; + 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(); + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40585")] + public async Task TestYieldBreak() + { + await TestAsync(""" + using System.Collections; - class Program + class Program + { + public static IEnumerable Method(bool condition) { - public static IEnumerable Method(bool condition) + [||]if (condition) { - [||]if (condition) - { - yield return 1; - } + yield return 1; } } - """, - """ - using System.Collections; + } + """, """ + using System.Collections; - class Program + class Program + { + public static IEnumerable Method(bool condition) { - public static IEnumerable Method(bool condition) + if (!condition) { - if (!condition) - { - yield break; - } - yield return 1; + yield break; } + yield return 1; } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing() + { + await TestAsync(""" + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - [||]if (l == "hello") - { - return null; - } + [||]if (l == "hello") + { + return null; + } - return l; + return l; - } } - """, - """ - class C + } + """, """ + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - if (l != "hello") - { - return l; - } + if (l != "hello") + { + return l; + } - return null; + return null; - } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing_WithComments() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing_WithComments() + { + await TestAsync(""" + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - [||]if (l == "hello") - { - // null 1 - return null; // null 2 - // null 3 - } + [||]if (l == "hello") + { + // null 1 + return null; // null 2 + // null 3 + } - // l 1 - return l; // l 2 - // l 3 + // l 1 + return l; // l 2 + // l 3 - } } - """, - """ - class C + } + """, """ + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - if (l != "hello") - { - // l 1 - return l; // l 2 - // null 3 - } + if (l != "hello") + { + // l 1 + return l; // l 2 + // null 3 + } - // null 1 - return null; // null 2 - // l 3 + // null 1 + return null; // null 2 + // l 3 - } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing_NoTrivia() - { - await TestInRegularAndScriptAsync( - """ - class C - { - string? M(bool b) - {[||]if(b){return(true);}return(false);} - } - """, - """ - class C - { - string? M(bool b) - { if (!b) { return (false); } return (true); } - } - """); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing_NoTrivia() + { + await TestAsync(""" + class C + { + string? M(bool b) + {[||]if(b){return(true);}return(false);} + } + """, """ + class C + { + string? M(bool b) + { if (!b) { return (false); } return (true); } + } + """); } } diff --git a/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj index 801b6a5a0c0e4..73c7105b1f10e 100644 --- a/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj +++ b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj @@ -3,7 +3,7 @@ Library - $(NetRoslyn);net472 + $(NetRoslynNext);net472 Microsoft.CodeAnalysis.CSharp.UnitTests true diff --git a/src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs b/src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs index 09ef2850b043b..e2676d9da45ed 100644 --- a/src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs +++ b/src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs @@ -2,19 +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 System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.ReplaceConditionalWithStatements; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ReplaceConditionalWithStatements; using VerifyCS = CSharpCodeRefactoringVerifier; +[UseExportProvider] public class ReplaceConditionalWithStatementsTests { [Fact] diff --git a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs index 02d2a79a8fc74..c841e11de33b6 100644 --- a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs +++ b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs @@ -240,7 +240,7 @@ static ISymbol F(ISymbol s) AssertEx.Equal( $" at Program.<
$>g__F|0_1(ISymbol s) in {FeaturesResources.Query}:line 11" + Environment.NewLine + $" at Program.<>c.<
$>b__0_2(ISymbol x) in {FeaturesResources.Query}:line 5" + Environment.NewLine + - $" at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()" + Environment.NewLine, + $" at System.Linq.Enumerable.ArraySelectIterator`2.MoveNext()" + Environment.NewLine, exception.StackTrace.JoinText()); } } diff --git a/src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs b/src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs index 022a140db17fe..570233da04cb8 100644 --- a/src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs +++ b/src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseNamedArguments { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)] public class UseNamedArgumentsTests { diff --git a/src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs b/src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs index d239bdf229bc9..8089f6dcd6f70 100644 --- a/src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs +++ b/src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs @@ -103,7 +103,7 @@ public async Task TestMissingOnExpression(string expression) await TestAllMissingOnExpressionAsync(expression); } - [InlineData("i == default || i > default(int)", "i is default(int) or > (default(int))")] + [InlineData("i == default || i > default(int)", "i is default(int) or > default(int)")] [InlineData("!(o is C c)", "o is not C c")] [InlineData("o is int ii && o is long jj", "o is int ii and long jj")] [InlineData("o is string || o is Exception", "o is string or Exception")] diff --git a/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs b/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs index 1d57cf4fa0694..4b859cd985342 100644 --- a/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseRec { using VerifyCS = CSharpCodeRefactoringVerifier; + [UseExportProvider] [Trait(Traits.Feature, Traits.Features.CodeActionsUseRecursivePatterns)] public class UseRecursivePatternsRefactoringTests { diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 99322262f53c0..fc9ea2a994409 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -24,6 +24,7 @@ using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -57,7 +58,7 @@ public abstract Task ChangeSignatureAsync( LineFormattingOptionsProvider fallbackOptions, CancellationToken cancellationToken); - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); protected abstract T TransferLeadingWhitespaceTrivia(T newArgument, SyntaxNode oldArgument) where T : SyntaxNode; @@ -414,17 +415,23 @@ private static async Task> FindChangeSignatureR } // Update the documents using the updated syntax trees - foreach (var docId in nodesToUpdate.Keys) - { - var updatedDoc = currentSolution.GetRequiredDocument(docId).WithSyntaxRoot(updatedRoots[docId]); - var cleanupOptions = await updatedDoc.GetCodeCleanupOptionsAsync(context.FallbackOptions, cancellationToken).ConfigureAwait(false); + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: nodesToUpdate.Keys, + produceItems: static async (docId, callback, args, cancellationToken) => + { + var updatedDoc = args.currentSolution.GetRequiredDocument(docId).WithSyntaxRoot(args.updatedRoots[docId]); + var cleanupOptions = await updatedDoc.GetCodeCleanupOptionsAsync(args.context.FallbackOptions, cancellationToken).ConfigureAwait(false); - var docWithImports = await ImportAdder.AddImportsFromSymbolAnnotationAsync(updatedDoc, cleanupOptions.AddImportOptions, cancellationToken).ConfigureAwait(false); - var reducedDoc = await Simplifier.ReduceAsync(docWithImports, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken: cancellationToken).ConfigureAwait(false); - var formattedDoc = await Formatter.FormatAsync(reducedDoc, SyntaxAnnotation.ElasticAnnotation, cleanupOptions.FormattingOptions, cancellationToken).ConfigureAwait(false); + var docWithImports = await ImportAdder.AddImportsFromSymbolAnnotationAsync(updatedDoc, cleanupOptions.AddImportOptions, cancellationToken).ConfigureAwait(false); + var reducedDoc = await Simplifier.ReduceAsync(docWithImports, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + var formattedDoc = await Formatter.FormatAsync(reducedDoc, SyntaxAnnotation.ElasticAnnotation, cleanupOptions.FormattingOptions, cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentSyntaxRoot(docId, (await formattedDoc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!); - } + callback((formattedDoc.Id, await formattedDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); + }, + args: (currentSolution, updatedRoots, context), + cancellationToken).ConfigureAwait(false); + + currentSolution = currentSolution.WithDocumentSyntaxRoots(changedDocuments); telemetryTimer.Stop(); ChangeSignatureLogger.LogCommitInformation(telemetryNumberOfDeclarationsToUpdate, telemetryNumberOfReferencesToUpdate, telemetryTimer.Elapsed); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs index 7fb1b6b5b54ab..78d6303864837 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs @@ -240,11 +240,8 @@ private static async Task TryMergeFixesAsync( // Finally, apply the changes to each document to the solution, producing the // new solution. - var currentSolution = oldSolution; - foreach (var (documentId, finalText) in documentIdToFinalText) - currentSolution = currentSolution.WithDocumentText(documentId, finalText); - - return currentSolution; + var finalSolution = oldSolution.WithDocumentTexts(documentIdToFinalText); + return finalSolution; } private static async Task>> GetDocumentIdToChangedDocumentsAsync( @@ -269,7 +266,7 @@ private static async Task TryMergeFixesAsync( return documentIdToChangedDocuments; } - private static async Task> GetDocumentIdToFinalTextAsync( + private static async Task> GetDocumentIdToFinalTextAsync( Solution oldSolution, IReadOnlyDictionary> documentIdToChangedDocuments, ImmutableArray<(Diagnostic diagnostic, CodeAction action)> diagnosticsAndCodeActions, @@ -291,7 +288,7 @@ private static async Task> GetDocume } await Task.WhenAll(getFinalDocumentTasks).ConfigureAwait(false); - return documentIdToFinalText; + return documentIdToFinalText.SelectAsArray(kvp => (kvp.Key, kvp.Value)); } private static async Task GetFinalDocumentTextAsync( diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index b9dd43e57681d..9630a575205ff 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -24,6 +24,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -487,32 +488,25 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n var refLocationGroups = refLocationsInSolution.GroupBy(loc => loc.Document.Id); - var fixedDocuments = await Task.WhenAll( - refLocationGroups.Select(refInOneDocument => - FixReferencingDocumentAsync( - solutionWithChangedNamespace.GetRequiredDocument(refInOneDocument.Key), + var fixedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: refLocationGroups, + produceItems: static async (refInOneDocument, callback, args, cancellationToken) => + { + var result = await FixReferencingDocumentAsync( + args.solutionWithChangedNamespace.GetRequiredDocument(refInOneDocument.Key), refInOneDocument, - newNamespace, - fallbackOptions, - cancellationToken))).ConfigureAwait(false); - - var solutionWithFixedReferences = await MergeDocumentChangesAsync(solutionWithChangedNamespace, fixedDocuments, cancellationToken).ConfigureAwait(false); + args.newNamespace, + args.fallbackOptions, + cancellationToken).ConfigureAwait(false); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); + }, + args: (solutionWithChangedNamespace, newNamespace, fallbackOptions), + cancellationToken).ConfigureAwait(false); + var solutionWithFixedReferences = solutionWithChangedNamespace.WithDocumentSyntaxRoots(fixedDocuments); return (solutionWithFixedReferences, refLocationGroups.SelectAsArray(g => g.Key)); } - private static async Task MergeDocumentChangesAsync(Solution originalSolution, Document[] changedDocuments, CancellationToken cancellationToken) - { - foreach (var document in changedDocuments) - { - originalSolution = originalSolution.WithDocumentSyntaxRoot( - document.Id, - await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); - } - - return originalSolution; - } - private readonly struct LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod) { public ReferenceLocation ReferenceLocation { get; } = location; @@ -773,40 +767,39 @@ private static async Task RemoveUnnecessaryImportsAsync( CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { - using var _ = PooledHashSet.GetInstance(out var linkedDocumentsToSkip); - var documentsToProcessBuilder = ArrayBuilder.GetInstance(); + using var _1 = PooledHashSet.GetInstance(out var linkedDocumentsToSkip); + using var _2 = ArrayBuilder.GetInstance(out var documentsToProcess); foreach (var id in ids) { if (linkedDocumentsToSkip.Contains(id)) - { continue; - } var document = solution.GetRequiredDocument(id); linkedDocumentsToSkip.AddRange(document.GetLinkedDocumentIds()); - documentsToProcessBuilder.Add(document); - - document = await RemoveUnnecessaryImportsWorkerAsync( - document, - CreateImports(document, names, withFormatterAnnotation: false), - cancellationToken).ConfigureAwait(false); - solution = document.Project.Solution; + documentsToProcess.Add(document); } - var documentsToProcess = documentsToProcessBuilder.ToImmutableAndFree(); - - var changeDocuments = await Task.WhenAll(documentsToProcess.Select( - doc => RemoveUnnecessaryImportsWorkerAsync( + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: documentsToProcess, + produceItems: static async (doc, callback, args, cancellationToken) => + { + var result = await RemoveUnnecessaryImportsWorkerAsync( doc, - CreateImports(doc, names, withFormatterAnnotation: false), - cancellationToken))).ConfigureAwait(false); + CreateImports(doc, args.names, withFormatterAnnotation: false), + args.fallbackOptions, + cancellationToken).ConfigureAwait(false); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); + }, + args: (names, fallbackOptions), + cancellationToken).ConfigureAwait(false); - return await MergeDocumentChangesAsync(solution, changeDocuments, cancellationToken).ConfigureAwait(false); + return solution.WithDocumentSyntaxRoots(changedDocuments); - async Task RemoveUnnecessaryImportsWorkerAsync( + async static Task RemoveUnnecessaryImportsWorkerAsync( Document doc, IEnumerable importsToRemove, + CodeCleanupOptionsProvider fallbackOptions, CancellationToken token) { var removeImportService = doc.GetRequiredLanguageService(); 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 979bed9aade96..3fc61edaab320 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; @@ -87,43 +88,44 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke try { // Find applicable symbols in parallel - using var _1 = ArrayBuilder?>>.GetInstance(out var tasks); + var peReferenceMethodSymbolsTask = ProducerConsumer.RunParallelAsync( + source: GetAllRelevantPeReferences(_originatingDocument.Project), + produceItems: static (peReference, callback, args, cancellationToken) => + args.@this.GetExtensionMethodSymbolsFromPeReferenceAsync(peReference, callback, args.forceCacheCreation, cancellationToken), + args: (@this: this, forceCacheCreation), + cancellationToken); + + var projectMethodSymbolsTask = ProducerConsumer.RunParallelAsync( + source: GetAllRelevantProjects(_originatingDocument.Project), + produceItems: static (project, callback, args, cancellationToken) => + args.@this.GetExtensionMethodSymbolsFromProjectAsync(project, callback, args.forceCacheCreation, cancellationToken), + args: (@this: this, forceCacheCreation), + cancellationToken); + + var results = await Task.WhenAll(peReferenceMethodSymbolsTask, projectMethodSymbolsTask).ConfigureAwait(false); - foreach (var peReference in GetAllRelevantPeReferences(_originatingDocument.Project)) - { - tasks.Add(Task.Run(() => GetExtensionMethodSymbolsFromPeReferenceAsync( - peReference, - forceCacheCreation, - cancellationToken).AsTask(), cancellationToken)); - } - - foreach (var project in GetAllRelevantProjects(_originatingDocument.Project)) - { - tasks.Add(Task.Run(() => GetExtensionMethodSymbolsFromProjectAsync( - project, - forceCacheCreation, - cancellationToken), cancellationToken)); - } - - using var _2 = ArrayBuilder.GetInstance(out var symbols); var isPartialResult = false; - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - foreach (var result in results) + using var _ = ArrayBuilder.GetInstance(results[0].Length + results[1].Length, out var symbols); + foreach (var methodArray in results) { - // `null` indicates we don't have the index ready for the corresponding project/PE. - // returns what we have even it means we only show partial results. - if (result == null) + foreach (var method in methodArray) { - isPartialResult = true; - continue; + // `null` indicates we don't have the index ready for the corresponding project/PE. + // returns what we have even it means we only show partial results. + if (method is null) + { + isPartialResult = true; + } + else + { + symbols.Add(method); + } } - - symbols.AddRange(result); } - var browsableSymbols = symbols.ToImmutable() + var browsableSymbols = symbols + .ToImmutable() .FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, _originatingSemanticModel.Compilation); return (browsableSymbols, isPartialResult); @@ -148,8 +150,9 @@ private static ImmutableArray GetAllRelevantProjects(Project project) private static ImmutableArray GetAllRelevantPeReferences(Project project) => project.MetadataReferences.OfType().ToImmutableArray(); - private async Task?> GetExtensionMethodSymbolsFromProjectAsync( + private async Task GetExtensionMethodSymbolsFromProjectAsync( Project project, + Action callback, bool forceCacheCreation, CancellationToken cancellationToken) { @@ -161,13 +164,12 @@ private static ImmutableArray GetAllRelevantPeRefer else if (!_cacheService.ProjectItemsCache.TryGetValue(project.Id, out cacheEntry)) { // Use cached data if available, even checksum doesn't match. otherwise, returns null indicating cache not ready. - return null; + callback(null); + return; } if (!cacheEntry.ContainsExtensionMethod) - { - return ImmutableArray.Empty; - } + return; var originatingAssembly = _originatingSemanticModel.Compilation.Assembly; var filter = CreateAggregatedFilter(cacheEntry); @@ -183,13 +185,19 @@ private static ImmutableArray GetAllRelevantPeRefer var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly( compilation.Assembly, filter, internalsVisible, cancellationToken); - return project == _originatingDocument.Project - ? GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken) - : GetExtensionMethodsForSymbolsFromDifferentCompilation(matchingMethodSymbols, cancellationToken); + if (project == _originatingDocument.Project) + { + GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, callback, cancellationToken); + } + else + { + GetExtensionMethodsForSymbolsFromDifferentCompilation(matchingMethodSymbols, callback, cancellationToken); + } } - private async ValueTask?> GetExtensionMethodSymbolsFromPeReferenceAsync( + private async Task GetExtensionMethodSymbolsFromPeReferenceAsync( PortableExecutableReference peReference, + Action callback, bool forceCacheCreation, CancellationToken cancellationToken) { @@ -210,7 +218,8 @@ private static ImmutableArray GetAllRelevantPeRefer else { // No cached data immediately available, returns null to indicate index not ready - return null; + callback(null); + return; } } @@ -218,7 +227,7 @@ private static ImmutableArray GetAllRelevantPeRefer !symbolInfo.ContainsExtensionMethod || _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is not IAssemblySymbol assembly) { - return ImmutableArray.Empty; + return; } var filter = CreateAggregatedFilter(symbolInfo); @@ -226,15 +235,14 @@ private static ImmutableArray GetAllRelevantPeRefer var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken); - return GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken); + GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, callback, cancellationToken); } - private ImmutableArray GetExtensionMethodsForSymbolsFromDifferentCompilation( + private void GetExtensionMethodsForSymbolsFromDifferentCompilation( MultiDictionary matchingMethodSymbols, + Action callback, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); - // Matching extension method symbols are grouped based on their receiver type. foreach (var (declaredReceiverType, methodSymbols) in matchingMethodSymbols) { @@ -292,21 +300,16 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromDifferent } if (_originatingSemanticModel.IsAccessible(_position, methodInOriginatingCompilation)) - { - builder.Add(methodInOriginatingCompilation); - } + callback(methodInOriginatingCompilation); } } - - return builder.ToImmutableAndClear(); } - private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompilation( + private void GetExtensionMethodsForSymbolsFromSameCompilation( MultiDictionary matchingMethodSymbols, + Action callback, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); - // Matching extension method symbols are grouped based on their receiver type. foreach (var (receiverType, methodSymbols) in matchingMethodSymbols) { @@ -315,9 +318,7 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompi // If we already checked an extension method with same receiver type before, and we know it can't be applied // to the receiverTypeSymbol, then no need to proceed further. if (_checkedReceiverTypes.TryGetValue(receiverType, out var cachedResult) && !cachedResult) - { continue; - } // We haven't seen this type yet. Try to check by reducing one extension method // to the given receiver type and save the result. @@ -335,14 +336,10 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompi foreach (var methodSymbol in methodSymbols) { if (_originatingSemanticModel.IsAccessible(_position, methodSymbol)) - { - builder.Add(methodSymbol); - } + callback(methodSymbol); } } } - - return builder.ToImmutableAndClear(); } private MultiDictionary GetPotentialMatchingSymbolsFromAssembly( diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 5715e2012bebf..82afe2421891b 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -21,7 +21,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Rename; -using Microsoft.CodeAnalysis.Rename.ConflictEngine; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; @@ -31,8 +30,16 @@ namespace Microsoft.CodeAnalysis.EncapsulateField; internal abstract partial class AbstractEncapsulateFieldService : ILanguageService { + private static readonly CultureInfo EnUSCultureInfo = new("en-US"); + private static readonly SymbolRenameOptions s_symbolRenameOptions = new( + RenameOverloads: false, + RenameInStrings: false, + RenameInComments: false, + RenameFile: false); + protected abstract Task RewriteFieldNameAndAccessibilityAsync(string originalFieldName, bool makePrivate, Document document, SyntaxAnnotation declarationAnnotation, CodeAndImportGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken); protected abstract Task> GetFieldsAsync(Document document, TextSpan span, CancellationToken cancellationToken); + protected abstract IEnumerable GetConstructorNodes(INamedTypeSymbol containingType); public async Task EncapsulateFieldsInSpanAsync(Document document, TextSpan span, CleanCodeGenerationOptionsProvider fallbackOptions, bool useDefaultBehavior, CancellationToken cancellationToken) { @@ -44,7 +51,7 @@ public async Task EncapsulateFieldsInSpanAsync(Document return new EncapsulateFieldResult( firstField.ToDisplayString(), firstField.GetGlyph(), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, useDefaultBehavior, c)); + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, useDefaultBehavior, cancellationToken)); } public async Task> GetEncapsulateFieldCodeActionsAsync(Document document, TextSpan span, CleanCodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken) @@ -74,19 +81,16 @@ public async Task> GetEncapsulateFieldCodeActionsAsyn } private ImmutableArray EncapsulateAllFields(Document document, ImmutableArray fields, CleanCodeGenerationOptionsProvider fallbackOptions) - { - return - [ + => [ CodeAction.Create( - FeaturesResources.Encapsulate_fields_and_use_property, - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), - nameof(FeaturesResources.Encapsulate_fields_and_use_property)), + FeaturesResources.Encapsulate_fields_and_use_property, + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, cancellationToken), + nameof(FeaturesResources.Encapsulate_fields_and_use_property)), CodeAction.Create( FeaturesResources.Encapsulate_fields_but_still_use_field, - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, cancellationToken), nameof(FeaturesResources.Encapsulate_fields_but_still_use_field)), ]; - } private ImmutableArray EncapsulateOneField(Document document, IFieldSymbol field, CleanCodeGenerationOptionsProvider fallbackOptions) { @@ -94,12 +98,12 @@ private ImmutableArray EncapsulateOneField(Document document, IField return [ CodeAction.Create( - string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, field.Name), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), - nameof(FeaturesResources.Encapsulate_field_colon_0_and_use_property) + "_" + field.Name), + string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, field.Name), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, cancellationToken), + nameof(FeaturesResources.Encapsulate_field_colon_0_and_use_property) + "_" + field.Name), CodeAction.Create( string.Format(FeaturesResources.Encapsulate_field_colon_0_but_still_use_field, field.Name), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, cancellationToken), nameof(FeaturesResources.Encapsulate_field_colon_0_but_still_use_field) + "_" + field.Name), ]; } @@ -126,9 +130,7 @@ public async Task EncapsulateFieldsAsync( cancellationToken).ConfigureAwait(false); if (!result.HasValue) - { return solution; - } return await RemoteUtilities.UpdateSolutionAsync( solution, result.Value, cancellationToken).ConfigureAwait(false); @@ -182,23 +184,6 @@ private async Task EncapsulateFieldAsync( fieldDeclaration.GetSyntax(cancellationToken).WithAdditionalAnnotations(declarationAnnotation))); var solution = document.Project.Solution; - - foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) - { - var linkedDocument = solution.GetDocument(linkedDocumentId); - var linkedRoot = await linkedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var linkedFieldNode = linkedRoot.FindNode(fieldDeclaration.Span); - if (linkedFieldNode.Span != fieldDeclaration.Span) - { - continue; - } - - var updatedRoot = linkedRoot.ReplaceNode(linkedFieldNode, linkedFieldNode.WithAdditionalAnnotations(declarationAnnotation)); - solution = solution.WithDocumentSyntaxRoot(linkedDocumentId, updatedRoot); - } - - document = solution.GetDocument(document.Id); - // Resolve the annotated symbol and prepare for rename. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -220,18 +205,6 @@ private async Task EncapsulateFieldAsync( document = await Formatter.FormatAsync(document.WithSyntaxRoot(rewrittenFieldDeclaration), Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(false); - solution = document.Project.Solution; - foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) - { - var linkedDocument = solution.GetDocument(linkedDocumentId); - var linkedDocumentFormattingOptions = await linkedDocument.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); - var updatedLinkedRoot = await RewriteFieldNameAndAccessibilityAsync(finalFieldName, markFieldPrivate, linkedDocument, declarationAnnotation, fallbackOptions, cancellationToken).ConfigureAwait(false); - var updatedLinkedDocument = await Formatter.FormatAsync(linkedDocument.WithSyntaxRoot(updatedLinkedRoot), Formatter.Annotation, linkedDocumentFormattingOptions, cancellationToken).ConfigureAwait(false); - solution = updatedLinkedDocument.Project.Solution; - } - - document = solution.GetDocument(document.Id); - semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var newRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -262,11 +235,13 @@ private async Task UpdateReferencesAsync( bool updateReferences, Solution solution, Document document, IFieldSymbol field, string finalFieldName, string generatedPropertyName, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { if (!updateReferences) - { return solution; - } var projectId = document.Project.Id; + var linkedDocumentIds = document.GetLinkedDocumentIds(); + using var _ = PooledHashSet.GetInstance(out var linkedProjectIds); + linkedProjectIds.AddRange(linkedDocumentIds.Select(d => d.ProjectId)); + if (field.IsReadOnly) { // Inside the constructor we want to rename references the field to the final field name. @@ -274,9 +249,8 @@ private async Task UpdateReferencesAsync( if (finalFieldName != field.Name && constructorLocations.Count > 0) { solution = await RenameAsync( - solution, field, finalFieldName, - (docId, span) => IntersectsWithAny(docId, span, constructorLocations), - fallbackOptions, + solution, field, finalFieldName, fallbackOptions, linkedProjectIds, + filter: (docId, span) => IntersectsWithAny(docId, span, constructorLocations), cancellationToken).ConfigureAwait(false); document = solution.GetDocument(document.Id); @@ -288,16 +262,17 @@ private async Task UpdateReferencesAsync( // Outside the constructor we want to rename references to the field to final property name. return await RenameAsync( - solution, field, generatedPropertyName, - (documentId, span) => !IntersectsWithAny(documentId, span, constructorLocations), - fallbackOptions, + solution, field, generatedPropertyName, fallbackOptions, linkedProjectIds, + filter: (documentId, span) => !IntersectsWithAny(documentId, span, constructorLocations), cancellationToken).ConfigureAwait(false); } else { // Just rename everything. - return await Renamer.RenameSymbolAsync( - solution, field, new SymbolRenameOptions(), generatedPropertyName, cancellationToken).ConfigureAwait(false); + return await RenameAsync( + solution, field, generatedPropertyName, fallbackOptions, linkedProjectIds, + filter: static (documentId, span) => true, + cancellationToken).ConfigureAwait(false); } } @@ -305,21 +280,19 @@ private static async Task RenameAsync( Solution solution, IFieldSymbol field, string finalName, - Func filter, CodeCleanupOptionsProvider fallbackOptions, + HashSet linkedProjectIds, + Func filter, CancellationToken cancellationToken) { - var options = new SymbolRenameOptions( - RenameOverloads: false, - RenameInStrings: false, - RenameInComments: false, - RenameFile: false); - var initialLocations = await Renamer.FindRenameLocationsAsync( - solution, field, options, cancellationToken).ConfigureAwait(false); + solution, field, s_symbolRenameOptions, cancellationToken).ConfigureAwait(false); - var resolution = await initialLocations.Filter(filter).ResolveConflictsAsync( - field, finalName, nonConflictSymbolKeys: default, fallbackOptions, cancellationToken).ConfigureAwait(false); + // Ensure we don't update any files in projects linked to us. That will be taken care of automatically when we + // edit the files in the current project + var resolution = await initialLocations + .Filter((documentId, span) => !linkedProjectIds.Contains(documentId.ProjectId) && filter(documentId, span)) + .ResolveConflictsAsync(field, finalName, nonConflictSymbolKeys: default, fallbackOptions, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(resolution.IsSuccessful); @@ -343,8 +316,6 @@ private static bool IntersectsWithAny(DocumentId documentId, TextSpan span, ISet private ISet<(DocumentId documentId, TextSpan span)> GetConstructorLocations(Solution solution, INamedTypeSymbol containingType) => GetConstructorNodes(containingType).Select(n => (solution.GetRequiredDocument(n.SyntaxTree).Id, n.Span)).ToSet(); - internal abstract IEnumerable GetConstructorNodes(INamedTypeSymbol containingType); - protected static async Task AddPropertyAsync( Document document, Solution destinationSolution, @@ -465,6 +436,4 @@ protected static string GeneratePropertyName(string fieldName) var firstCharacter = EnUSCultureInfo.TextInfo.ToUpper(baseName[0]); return firstCharacter.ToString() + baseName[1..]; } - - private static readonly CultureInfo EnUSCultureInfo = new("en-US"); } diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs index e39103ea0b9c2..1d835f2358fea 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs @@ -28,12 +28,12 @@ protected abstract bool TryWrapWithUnchecked( public async Task FormatDocumentAsync(Document document, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var rules = new List { new FormatLargeBinaryExpressionRule(document.GetRequiredLanguageService()) }; - rules.AddRange(Formatter.GetDefaultFormattingRules(document)); - + var formatBinaryRule = new FormatLargeBinaryExpressionRule(document.GetRequiredLanguageService()); var formattedDocument = await Formatter.FormatAsync( document, s_specializedFormattingAnnotation, - options, rules, cancellationToken).ConfigureAwait(false); + options, + [formatBinaryRule, .. Formatter.GetDefaultFormattingRules(document)], + cancellationToken).ConfigureAwait(false); return formattedDocument; } diff --git a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs index b8670f91f4d2a..5bb83805b1e99 100644 --- a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs +++ b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.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.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,16 +15,17 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSymbolService { - protected abstract ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation); + protected abstract Task FindRelatedExplicitlyDeclaredSymbolAsync(Project project, ISymbol symbol, CancellationToken cancellationToken); protected abstract int? GetTargetPositionIfControlFlow(SemanticModel semanticModel, SyntaxToken token); - public async Task<(ISymbol?, Project, TextSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken) + public async Task<(ISymbol? symbol, Project project, TextSpan boundSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken) { var project = document.Project; var services = document.Project.Solution.Services; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // We don't need nullable information to compute the symbol. So avoid expensive work computing this. + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); var semanticInfo = await SymbolFinder.GetSemanticInfoAtPositionAsync(semanticModel, position, services, cancellationToken).ConfigureAwait(false); // Prefer references to declarations. It's more likely that the user is attempting to @@ -58,9 +58,8 @@ internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSym project = mapping.Project; } - // The compilation will have already been realised, either by the semantic model or the symbol mapping - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - return (FindRelatedExplicitlyDeclaredSymbol(symbol, compilation), project, semanticInfo.Span); + var explicitlyDeclaredSymbol = await FindRelatedExplicitlyDeclaredSymbolAsync(project, symbol, cancellationToken).ConfigureAwait(false); + return (explicitlyDeclaredSymbol, project, semanticInfo.Span); } public async Task<(int? targetPosition, TextSpan tokenSpan)> GetTargetIfControlFlowAsync(Document document, int position, CancellationToken cancellationToken) @@ -72,7 +71,8 @@ internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSym if (token == default) return default; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // We don't need nullable information to compute control flow targets. So avoid expensive work computing this. + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); return (GetTargetPositionIfControlFlow(semanticModel, token), token.Span); } diff --git a/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs b/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs index c2353232129a8..5076dc0268dd7 100644 --- a/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs +++ b/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; internal interface IGoToDefinitionSymbolService : ILanguageService { - Task<(ISymbol?, Project, TextSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken); + Task<(ISymbol? symbol, Project project, TextSpan boundSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken); /// /// If the position is on a control flow keyword (continue, break, yield, return , etc), returns the relevant position in the corresponding control flow statement. diff --git a/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs b/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs index ede4efcdcb5d3..7779de2fa8b44 100644 --- a/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs +++ b/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -552,10 +553,14 @@ private async Task GetChangedCallerAsync(Document document, // After: // void Caller() { var x = ((Func)(() => 1))(); } // Func Callee() { return () => 1; } + // + // Also, ensure that the node is formatted properly at the destination location. This is needed as the + // location of the destination node might be very different (indentation/nesting wise) from the original + // method where the inlined code is coming from. inlineExpression = (TExpressionSyntax)syntaxGenerator.AddParentheses( syntaxGenerator.CastExpression( GenerateTypeSyntax(calleeMethodSymbol.ReturnType, allowVar: false), - syntaxGenerator.AddParentheses(inlineMethodContext.InlineExpression))); + syntaxGenerator.AddParentheses(inlineMethodContext.InlineExpression.WithAdditionalAnnotations(Formatter.Annotation)))); } diff --git a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs index 9ad70f7081ced..03f44f9dc54bd 100644 --- a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.IntroduceParameter; @@ -241,17 +242,26 @@ private async Task IntroduceParameterAsync(Document originalDocument, var rewriter = new IntroduceParameterDocumentRewriter(this, originalDocument, expression, methodSymbol, containingMethod, selectedCodeAction, fallbackOptions, allOccurrences); - foreach (var (project, projectCallSites) in methodCallSites.GroupBy(kvp => kvp.Key.Project)) - { - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - foreach (var (document, invocations) in projectCallSites) + var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: methodCallSites.GroupBy(kvp => kvp.Key.Project), + produceItems: static async (tuple, callback, rewriter, cancellationToken) => { - var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false); - modifiedSolution = modifiedSolution.WithDocumentSyntaxRoot(document.Id, newRoot); - } - } - - return modifiedSolution; + var (project, projectCallSites) = tuple; + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + await RoslynParallel.ForEachAsync( + projectCallSites, + cancellationToken, + async (tuple, cancellationToken) => + { + var (document, invocations) = tuple; + var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false); + callback((document.Id, newRoot)); + }).ConfigureAwait(false); + }, + args: rewriter, + cancellationToken).ConfigureAwait(false); + + return modifiedSolution.WithDocumentSyntaxRoots(changedRoots); } /// diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs index 89515c43368be..0ae59492bed8c 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable; internal partial class AbstractIntroduceVariableService { - internal abstract class AbstractIntroduceVariableCodeAction : CodeAction + private abstract class AbstractIntroduceVariableCodeAction : CodeAction { private readonly bool _allOccurrences; private readonly bool _isConstant; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs index e7cbeb85533ae..ab8443bec9cff 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs @@ -2,27 +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. -#nullable disable - using Microsoft.CodeAnalysis.CodeCleanup; namespace Microsoft.CodeAnalysis.IntroduceVariable; internal partial class AbstractIntroduceVariableService { - private class IntroduceVariableCodeAction : AbstractIntroduceVariableCodeAction + private sealed class IntroduceVariableCodeAction( + TService service, + SemanticDocument document, + CodeCleanupOptions options, + TExpressionSyntax expression, + bool allOccurrences, + bool isConstant, + bool isLocal, + bool isQueryLocal) : AbstractIntroduceVariableCodeAction( + service, document, options, expression, allOccurrences, isConstant, isLocal, isQueryLocal) { - internal IntroduceVariableCodeAction( - TService service, - SemanticDocument document, - CodeCleanupOptions options, - TExpressionSyntax expression, - bool allOccurrences, - bool isConstant, - bool isLocal, - bool isQueryLocal) - : base(service, document, options, expression, allOccurrences, isConstant, isLocal, isQueryLocal) - { - } } } diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs index 7800caa54892a..258785ef00557 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs @@ -2,40 +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. -#nullable disable - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CaseCorrection; using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.IntroduceVariable; internal partial class AbstractIntroduceVariableService { - private class IntroduceVariableAllOccurrenceCodeAction : AbstractIntroduceVariableCodeAction + private class IntroduceVariableAllOccurrenceCodeAction( + TService service, + SemanticDocument document, + CodeCleanupOptions options, + TExpressionSyntax expression, + bool allOccurrences, + bool isConstant, + bool isLocal, + bool isQueryLocal) : AbstractIntroduceVariableCodeAction( + service, document, options, expression, allOccurrences, isConstant, isLocal, isQueryLocal) { - internal IntroduceVariableAllOccurrenceCodeAction( - TService service, - SemanticDocument document, - CodeCleanupOptions options, - TExpressionSyntax expression, - bool allOccurrences, - bool isConstant, - bool isLocal, - bool isQueryLocal) - : base(service, document, options, expression, allOccurrences, isConstant, isLocal, isQueryLocal) - { - } - - protected override async Task PostProcessChangesAsync(Document document, CancellationToken cancellationToken) - { - document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, Options.SimplifierOptions, cancellationToken).ConfigureAwait(false); - document = await Formatter.FormatAsync(document, Formatter.Annotation, Options.FormattingOptions, cancellationToken).ConfigureAwait(false); - document = await CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken).ConfigureAwait(false); - return document; - } } } diff --git a/src/Features/Core/Portable/IntroduceVariable/IntroduceVariableCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceVariable/IntroduceVariableCodeRefactoringProvider.cs index 121a6645a8aec..b6755b077c613 100644 --- a/src/Features/Core/Portable/IntroduceVariable/IntroduceVariableCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceVariable/IntroduceVariableCodeRefactoringProvider.cs @@ -15,16 +15,11 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable; [ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.ConvertAnonymousTypeToClass)] [ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.InvertConditional)] [ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.InvertLogical)] -[ExportCodeRefactoringProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, - Name = PredefinedCodeRefactoringProviderNames.IntroduceVariable), Shared] -internal class IntroduceVariableCodeRefactoringProvider : CodeRefactoringProvider +[ExportCodeRefactoringProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeRefactoringProviderNames.IntroduceVariable), Shared] +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed class IntroduceVariableCodeRefactoringProvider() : CodeRefactoringProvider { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public IntroduceVariableCodeRefactoringProvider() - { - } - public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, textSpan, cancellationToken) = context; diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs index 7bf7c7cfc4da7..eda6341df098a 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.ComponentModel; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.MetadataAsSource; @@ -18,7 +19,7 @@ protected abstract class CompatAbstractMetadataFormattingRule : AbstractMetadata #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member [Obsolete("Do not call this method directly (it will Stack Overflow).", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] - public sealed override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public sealed override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { var nextOperationCopy = nextOperation; AddSuppressOperationsSlow(list, node, ref nextOperationCopy); @@ -73,7 +74,7 @@ public sealed override AdjustSpacesOperation GetAdjustSpacesOperation(in SyntaxT /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperationsSlow(List list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperationsSlow(ArrayBuilder list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) => base.AddSuppressOperations(list, node, in nextOperation); /// diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 874bd38c45b91..912efe1b8e8bf 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -85,7 +85,7 @@ public async Task AddSourceToAsync( /// /// provide formatting rules to be used when formatting MAS file /// - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); /// /// Prepends a region directive at the top of the document with a name containing diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index 0c45abfa34f00..a967f30ae4c55 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs @@ -288,12 +288,9 @@ private static async Task PropagateChangeToLinkedDocumentsAsync(Docume var formattedText = await formattedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var solution = formattedDocument.Project.Solution; - foreach (var documentId in formattedDocument.GetLinkedDocumentIds()) - { - solution = solution.WithDocumentText(documentId, formattedText); - } - - return solution; + var finalSolution = solution.WithDocumentTexts( + formattedDocument.GetLinkedDocumentIds().SelectAsArray(id => (id, formattedText))); + return finalSolution; } private static string GetNewSymbolName(ISymbol symbol, string targetNamespace) diff --git a/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs b/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs index 1646c549e47cb..ea2a0b7184685 100644 --- a/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs +++ b/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs @@ -13,6 +13,11 @@ internal sealed class SymbolSearchUpdateNoOpEngine : ISymbolSearchUpdateEngine { public static readonly SymbolSearchUpdateNoOpEngine Instance = new(); + public void Dispose() + { + // Nothing to do for the no-op version. + } + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) => ValueTaskFactory.FromResult(ImmutableArray.Empty); diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs index d827000bfa327..e5db69773fdd4 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs @@ -65,6 +65,11 @@ internal SymbolSearchUpdateEngine( _reportAndSwallowExceptionUnlessCanceled = reportAndSwallowExceptionUnlessCanceled; } + public void Dispose() + { + // Nothing to do for the core symbol search engine. + } + public ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken) { diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 5baec78131adb..54fbfad723cda 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -41,7 +41,7 @@ public sealed override ImmutableArray FixableDiagnosticIds protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node); protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator); - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); protected abstract Task UpdatePropertyAsync( Document propertyDocument, Compilation compilation, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol, @@ -281,10 +281,8 @@ private static bool CanEditDocument( private async Task FormatAsync(SyntaxNode newRoot, Document document, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { var formattingRules = GetFormattingRules(document); - if (formattingRules == null) - { + if (formattingRules.IsDefault) return newRoot; - } var options = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); return Formatter.Format(newRoot, SpecializedFormattingAnnotation, document.Project.Solution.Services, options, formattingRules, cancellationToken); diff --git a/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs b/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs index 24a0440e06b94..a62f976c6c69b 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs +++ b/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking; @@ -186,10 +187,10 @@ private async Task TrackArgumentsAsync(ImmutableArray argume .Select(argument => (collector: Clone(), argument)) .ToImmutableArray(); - var tasks = collectorsAndArgumentMap - .Select(pair => Task.Run(() => pair.collector.VisitAsync(pair.argument, cancellationToken))); - - await Task.WhenAll(tasks).ConfigureAwait(false); + await RoslynParallel.ForEachAsync( + collectorsAndArgumentMap, + cancellationToken, + async (pair, cancellationToken) => await pair.collector.VisitAsync(pair.argument.Value, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); var items = collectorsAndArgumentMap .Select(pair => pair.collector.ProgressCollector) diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs index 8898f89aaaf22..1ddb360748015 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs @@ -118,9 +118,9 @@ protected override CodeRefactoringContext CreateCodeRefactoringContext(Document => new CodeRefactoringContext(document, span, (action, textSpan) => registerRefactoring(action), _sharedState.CodeActionOptions, cancellationToken); /// - /// The we want this test to run in. Defaults to if unspecified. + /// The we want this test to run in. Defaults to if unspecified. /// - public TestHost TestHost { get; set; } = TestHost.InProcess; + public TestHost TestHost { get; set; } = TestHost.OutOfProcess; private static readonly TestComposition s_editorFeaturesOOPComposition = FeaturesTestCompositions.Features.WithTestHostParts(TestHost.OutOfProcess); diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs index fb2c5a1637488..e8a1e4db8679a 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -104,9 +104,10 @@ protected override CodeRefactoringContext CreateCodeRefactoringContext(Document => new CodeRefactoringContext(document, span, (action, textSpan) => registerRefactoring(action), _sharedState.CodeActionOptions, cancellationToken); /// - /// The we want this test to run in. Defaults to if unspecified. + /// The we want this test to run in. Defaults to + /// if unspecified. /// - public TestHost TestHost { get; set; } = TestHost.InProcess; + public TestHost TestHost { get; set; } = TestHost.OutOfProcess; private static readonly TestComposition s_editorFeaturesOOPComposition = FeaturesTestCompositions.Features.WithTestHostParts(TestHost.OutOfProcess); diff --git a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs index 36d372197958c..6742e113a7814 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs @@ -80,7 +80,7 @@ internal TestParameters( bool retainNonFixableDiagnostics = false, bool includeDiagnosticsOutsideSelection = false, string title = null, - TestHost testHost = TestHost.InProcess, + TestHost testHost = TestHost.OutOfProcess, string workspaceKind = null, bool includeNonLocalDocumentDiagnostics = false, bool treatPositionIndicatorsAsCode = false) @@ -417,7 +417,7 @@ internal Task TestInRegularAndScriptAsync( object fixProviderData = null, ParseOptions parseOptions = null, string title = null, - TestHost testHost = TestHost.InProcess) + TestHost testHost = TestHost.OutOfProcess) { return TestInRegularAndScript1Async( initialMarkup, expectedMarkup, @@ -457,7 +457,7 @@ internal Task TestAsync( OptionsCollectionAlias globalOptions = null, object fixProviderData = null, CodeActionPriority? priority = null, - TestHost testHost = TestHost.InProcess) + TestHost testHost = TestHost.OutOfProcess) { return TestAsync( initialMarkup, diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest_NoEditor.cs index 1442d07a78fb9..a9197c7d2d880 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest_NoEditor.cs @@ -152,7 +152,7 @@ internal override async Task> GetDiagnosticsAsync( TestWorkspace workspace, TestParameters parameters) { var (analyzer, _) = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); var document = GetDocumentAndSelectSpan(workspace, out var span); var allDiagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(workspace, document, span, includeNonLocalDocumentDiagnostics: parameters.includeNonLocalDocumentDiagnostics); @@ -164,7 +164,7 @@ internal override async Task> GetDiagnosticsAsync( TestWorkspace workspace, TestParameters parameters) { var (analyzer, fixer) = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest_NoEditor.cs index 8009dd36e2267..30aaae6f4be0a 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionDiagnosticTest_NoEditor.cs @@ -68,7 +68,7 @@ internal override async Task> GetDiagnosticsAsync( TestWorkspace workspace, TestParameters parameters) { var (analyzer, _) = CreateDiagnosticProviderAndFixer(workspace); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); var document = GetDocumentAndSelectSpan(workspace, out var span); var diagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(workspace, document, span, includeNonLocalDocumentDiagnostics: parameters.includeNonLocalDocumentDiagnostics); @@ -79,7 +79,7 @@ internal override async Task> GetDiagnosticsAsync( TestWorkspace workspace, TestParameters parameters) { var (analyzer, fixer) = CreateDiagnosticProviderAndFixer(workspace); - AddAnalyzerToWorkspace(workspace, analyzer, parameters); + AddAnalyzerToWorkspace(workspace, analyzer); GetDocumentAndSelectSpanOrAnnotatedSpan(workspace, out var document, out var span, out var annotation); diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs index 90c53d22f1ead..52e1a2a1ff5a6 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUnncessarySuppressionDiagnosticTest.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests.Diagnostics; using Xunit.Abstractions; @@ -32,7 +33,7 @@ protected AbstractUnncessarySuppressionDiagnosticTest(ITestOutputHelper logger) private void AddAnalyzersToWorkspace(TestWorkspace workspace) { var analyzerReference = new AnalyzerImageReference(OtherAnalyzers.Add(SuppressionAnalyzer)); - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); + workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); } internal override async Task> GetDiagnosticsAsync( diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs index d19e283a4238c..803368302a6e4 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Remote.Testing; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -100,26 +101,23 @@ protected override async Task> GetDiagnosticsWorkerAs internal override Task GetCodeRefactoringAsync(TestWorkspace workspace, TestParameters parameters) => throw new NotImplementedException("No refactoring provided in diagnostic test"); - protected static void AddAnalyzerToWorkspace(Workspace workspace, DiagnosticAnalyzer analyzer, TestParameters parameters) + protected static void AddAnalyzerToWorkspace(Workspace workspace, DiagnosticAnalyzer analyzer) { - AnalyzerReference[] analyzeReferences; + AnalyzerReference[] analyzerReferences; if (analyzer != null) { - Contract.ThrowIfTrue(parameters.testHost == TestHost.OutOfProcess, $"Out-of-proc testing is not supported since {analyzer} can't be serialized."); - - analyzeReferences = new[] { new AnalyzerImageReference(ImmutableArray.Create(analyzer)) }; + var analyzerImageReference = new AnalyzerImageReference([analyzer]); + analyzerReferences = [analyzerImageReference]; } else { // create a serializable analyzer reference: - analyzeReferences = new[] - { + analyzerReferences = [ new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile), - new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile) - }; + new AnalyzerFileReference(DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic).GetType().Assembly.Location, TestAnalyzerAssemblyLoader.LoadFromFile)]; } - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(analyzeReferences)); + workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(analyzerReferences)); } protected static Document GetDocumentAndSelectSpan(TestWorkspace workspace, out TextSpan span) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 4be80e009e63a..a87b6604e47f6 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Concurrent; +using System.Text.Json; using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.FileWatching; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using StreamJsonRpc; using Xunit.Abstractions; using FileSystemWatcher = Roslyn.LanguageServer.Protocol.FileSystemWatcher; @@ -115,8 +114,8 @@ private static async Task WaitForFileWatcherAsync(TestLspServer testLspServer) private static FileSystemWatcher GetSingleFileWatcher(DynamicCapabilitiesRpcTarget dynamicCapabilities) { - var registrationJson = Assert.IsType(Assert.Single(dynamicCapabilities.Registrations).Value.RegisterOptions); - var registration = registrationJson.ToObject()!; + var registrationJson = Assert.IsType(Assert.Single(dynamicCapabilities.Registrations).Value.RegisterOptions); + var registration = JsonSerializer.Deserialize(registrationJson, ProtocolConversions.LspJsonSerializerOptions)!; return Assert.Single(registration.Watchers); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj index d1514de4c67c8..1ee7dd6eccb05 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj @@ -23,7 +23,7 @@ --> + Targets="GetPackInputs"> diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs index 49c5e48bb623f..504c980a16409 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs @@ -52,7 +52,8 @@ private TestLspServer(ExportProvider exportProvider, ILogger logger) var (clientStream, serverStream) = FullDuplexStream.CreatePair(); LanguageServerHost = new LanguageServerHost(serverStream, serverStream, exportProvider, logger); - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, new JsonMessageFormatter())) + var messageFormatter = LanguageServerHost.CreateJsonMessageFormatter(); + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, messageFormatter)) { AllowModificationWhileListening = true, ExceptionStrategy = ExceptionProcessing.ISerializable, diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs index bff88eebd4630..7060615bccbd6 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs @@ -2,7 +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 Microsoft.VisualStudio.Threading; using Nerdbank.Streams; +using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; @@ -10,7 +12,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; /// /// A wrapper which takes a service but actually sends calls to it through JsonRpc to ensure we can actually use the service across a wire. /// -internal sealed class BrokeredServiceProxy : IAsyncDisposable where T : class +internal sealed class BrokeredServiceProxy : System.IAsyncDisposable where T : class { /// /// A task that cane awaited to assert the rest of the fields in this class being assigned and non-null. @@ -25,8 +27,14 @@ public BrokeredServiceProxy(T service) { var (serverStream, clientStream) = FullDuplexStream.CreatePair(); - var serverTask = Task.Run(async () => + _createConnectionTask = Task.WhenAll(CreateServerAsync(), CreateClientAsync()); + return; + + async Task CreateServerAsync() { + // Always yield to ensure caller can proceed. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + var serverMultiplexingStream = await MultiplexingStream.CreateAsync(serverStream); var serverChannel = await serverMultiplexingStream.AcceptChannelAsync(""); @@ -35,10 +43,13 @@ public BrokeredServiceProxy(T service) _serverRpc.AddLocalRpcTarget(service, options: null); _serverRpc.StartListening(); - }); + } - var clientTask = Task.Run(async () => + async Task CreateClientAsync() { + // Always yield to ensure caller can proceed. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + var clientMultiplexingStream = await MultiplexingStream.CreateAsync(clientStream); var clientChannel = await clientMultiplexingStream.OfferChannelAsync(""); @@ -47,9 +58,7 @@ public BrokeredServiceProxy(T service) _clientFactoryProxy = _clientRpc.Attach(); _clientRpc.StartListening(); - }); - - _createConnectionTask = Task.WhenAll(serverTask, clientTask); + } } public async ValueTask DisposeAsync() diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs index 8c6d5c693539c..a0f0dbbca8ecc 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; @@ -32,10 +32,9 @@ Task INotificationHandler.HandleNotification return _serviceBrokerFactory.CreateAndConnectAsync(request.PipeName); } - [DataContract] private class NotificationParams { - [DataMember(Name = "pipeName")] + [JsonPropertyName("pipeName")] public required string PipeName { get; set; } } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs index 89a3613e08fe5..6cc5edc44e4f2 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs @@ -3,44 +3,40 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol; -[DataContract] internal class DidChangeWatchedFilesRegistrationOptions { - [DataMember(Name = "watchers")] + [JsonPropertyName("watchers")] public required FileSystemWatcher[] Watchers { get; set; } } -[DataContract] internal class FileSystemWatcher { - [DataMember(Name = "globPattern")] + [JsonPropertyName("globPattern")] public required RelativePattern GlobPattern { get; set; } - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public WatchKind? Kind { get; set; } } -[DataContract] internal class RelativePattern { - [DataMember(Name = "baseUri")] + [JsonPropertyName("baseUri")] [JsonConverter(typeof(DocumentUriConverter))] public required Uri BaseUri { get; set; } - [DataMember(Name = "pattern")] + [JsonPropertyName("pattern")] public required string Pattern { get; set; } } // The LSP specification has a spelling error in the protocol, but Microsoft.VisualStudio.LanguageServer.Protocol // didn't carry that error along. This corrects that. -[DataContract] internal class UnregistrationParamsWithMisspelling { - [DataMember(Name = "unregisterations")] + [JsonPropertyName("unregisterations")] public required Unregistration[] Unregistrations { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index f68f4a7e7d536..c920f014a8327 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -12,10 +12,10 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; @@ -160,23 +160,18 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList(); - - var projectsThatNeedRestore = new ConcurrentSet(); - foreach (var projectToLoad in projectPathsToLoadOrReload) - { - tasks.Add(Task.Run(async () => + var projectsThatNeedRestore = await ProducerConsumer.RunParallelAsync( + source: projectPathsToLoadOrReload, + produceItems: static async (projectToLoad, callback, args, cancellationToken) => { - var projectNeedsRestore = await LoadOrReloadProjectAsync(projectToLoad, toastErrorReporter, buildHostProcessManager, cancellationToken); + var projectNeedsRestore = await args.@this.LoadOrReloadProjectAsync( + projectToLoad, args.toastErrorReporter, args.buildHostProcessManager, cancellationToken); if (projectNeedsRestore) - { - projectsThatNeedRestore.Add(projectToLoad.Path); - } - }, cancellationToken)); - } - - await Task.WhenAll(tasks); + callback(projectToLoad.Path); + }, + args: (@this: this, toastErrorReporter, buildHostProcessManager), + cancellationToken).ConfigureAwait(false); if (_globalOptionService.GetOption(LanguageServerProjectSystemOptionsStorage.EnableAutomaticRestore) && projectsThatNeedRestore.Any()) { @@ -185,7 +180,7 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList.HandleNotification return _projectSystem.OpenProjectsAsync(request.Projects.SelectAsArray(p => p.LocalPath)); } - [DataContract] private class NotificationParams { - [DataMember(Name = "projects")] + [JsonPropertyName("projects")] public required Uri[] Projects { get; set; } } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs index da7bf9f0c814b..198c329b2ad96 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs @@ -1,9 +1,9 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; @@ -31,10 +31,9 @@ Task INotificationHandler.HandleNotification return _projectSystem.OpenSolutionAsync(request.Solution.LocalPath); } - [DataContract] private class NotificationParams { - [DataMember(Name = "solution")] + [JsonPropertyName("solution")] public required Uri Solution { get; set; } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs index 8e7aa86e50b2d..ac0659d8c0df4 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.PooledObjects; @@ -120,20 +120,20 @@ static bool SatisfiesVersion(VersionRange requestedVersionRange, NuGetVersion pr } } - internal static async Task RestoreProjectsAsync(ImmutableHashSet projectPaths, CancellationToken cancellationToken) + internal static async Task RestoreProjectsAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) { if (projectPaths.IsEmpty) - { return; - } Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); + var languageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - var unresolvedParams = new UnresolvedDependenciesParams([.. projectPaths]); + + // Ensure we only pass unique paths back to be restored. + var unresolvedParams = new UnresolvedDependenciesParams([.. projectPaths.Distinct()]); await languageServerManager.SendRequestAsync(ProjectNeedsRestoreName, unresolvedParams, cancellationToken); } - [DataContract] private record UnresolvedDependenciesParams( - [property: DataMember(Name = "projectFilePaths")] string[] ProjectFilePaths); + [property: JsonPropertyName("projectFilePaths")] string[] ProjectFilePaths); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs index 97c89c8ff79f8..63f16c646f97a 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs @@ -2,7 +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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; @@ -12,17 +12,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; /// except for SdkVersion, which is unused by the client in the O# version. /// -[DataContract] internal record ProjectLoadTelemetryEvent( // The project guid (if it came from a solution), or a hash representing the file path and contents. - [property: DataMember(Name = "ProjectId")] string ProjectId, - [property: DataMember(Name = "SessionId")] string SessionId, - [property: DataMember(Name = "OutputKind")] int OutputKind, - [property: DataMember(Name = "ProjectCapabilities")] IEnumerable ProjectCapabilities, - [property: DataMember(Name = "TargetFrameworks")] IEnumerable TargetFrameworks, - [property: DataMember(Name = "References")] IEnumerable References, - [property: DataMember(Name = "FileExtensions")] IEnumerable FileExtensions, - [property: DataMember(Name = "FileCounts")] IEnumerable FileCounts, - [property: DataMember(Name = "SdkStyleProject")] bool SdkStyleProject) + [property: JsonPropertyName("ProjectId")] string ProjectId, + [property: JsonPropertyName("SessionId")] string SessionId, + [property: JsonPropertyName("OutputKind")] int OutputKind, + [property: JsonPropertyName("ProjectCapabilities")] IEnumerable ProjectCapabilities, + [property: JsonPropertyName("TargetFrameworks")] IEnumerable TargetFrameworks, + [property: JsonPropertyName("References")] IEnumerable References, + [property: JsonPropertyName("FileExtensions")] IEnumerable FileExtensions, + [property: JsonPropertyName("FileCounts")] IEnumerable FileCounts, + [property: JsonPropertyName("SdkStyleProject")] bool SdkStyleProject) { } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index fd5f36244b1f9..beae910355125 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; @@ -18,26 +18,23 @@ internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; - [DataContract] private class ProvideDynamicFileParams { - [DataMember(Name = "razorFiles")] + [JsonPropertyName("razorFiles")] public required Uri[] RazorFiles { get; set; } } - [DataContract] private class ProvideDynamicFileResponse { - [DataMember(Name = "generatedFiles")] + [JsonPropertyName("generatedFiles")] public required Uri[] GeneratedFiles { get; set; } } private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; - [DataContract] private class RemoveDynamicFileParams { - [DataMember(Name = "razorFiles")] + [JsonPropertyName("razorFiles")] public required Uri[] RazorFiles { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs index 82e2ab5fa530c..158a7f99d7604 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs @@ -2,12 +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.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; -[DataContract] internal class ProjectDebugConfiguration { public ProjectDebugConfiguration(string projectPath, string outputPath, string projectName, bool targetsDotnetCore, bool isExe, string? solutionPath) @@ -20,21 +18,21 @@ public ProjectDebugConfiguration(string projectPath, string outputPath, string p SolutionPath = solutionPath; } - [JsonProperty(PropertyName = "projectPath")] + [JsonPropertyName("projectPath")] public string ProjectPath { get; } - [JsonProperty(PropertyName = "outputPath")] + [JsonPropertyName("outputPath")] public string OutputPath { get; } - [JsonProperty(PropertyName = "projectName")] + [JsonPropertyName("projectName")] public string ProjectName { get; } - [JsonProperty(PropertyName = "targetsDotnetCore")] + [JsonPropertyName("targetsDotnetCore")] public bool TargetsDotnetCore { get; } - [JsonProperty(PropertyName = "isExe")] + [JsonPropertyName("isExe")] public bool IsExe { get; } - [JsonProperty(PropertyName = "solutionPath")] + [JsonPropertyName("solutionPath")] public string? SolutionPath { get; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs index 1d3b795be6ba1..ac806d4fe897b 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs @@ -2,12 +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.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; -[DataContract] internal record WorkspaceDebugConfigurationParams( - [JsonProperty(PropertyName = "workspacePath"), JsonConverter(typeof(DocumentUriConverter))] Uri WorkspacePath); + [property: JsonPropertyName("workspacePath"), JsonConverter(typeof(DocumentUriConverter))] Uri WorkspacePath); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs index 44bf12762fc65..2717d9c21f953 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs @@ -2,19 +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.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] internal sealed record RestoreParams( // An empty set of project file paths means restore all projects in the workspace. - [property: DataMember(Name = "projectFilePaths")] string[] ProjectFilePaths + [property: JsonPropertyName("projectFilePaths")] string[] ProjectFilePaths ) : IPartialResultParams { - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs index 40731e53c9d9d..52a26da7932ca 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs @@ -2,12 +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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] internal sealed record RestorePartialResult( - [property: DataMember(Name = "stage")] string Stage, - [property: DataMember(Name = "message")] string Message + [property: JsonPropertyName("stage")] string Stage, + [property: JsonPropertyName("message")] string Message ); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs index 013a155b73eb5..d0cca0eb209bc 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs @@ -7,6 +7,7 @@ using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; +using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer; @@ -28,7 +29,8 @@ internal sealed class LanguageServerHost public LanguageServerHost(Stream inputStream, Stream outputStream, ExportProvider exportProvider, ILogger logger) { - var messageFormatter = new JsonMessageFormatter(); + var messageFormatter = CreateJsonMessageFormatter(); + var handler = new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter); // If there is a jsonrpc disconnect or server shutdown, that is handled by the AbstractLanguageServer. No need to do anything here. @@ -44,7 +46,15 @@ public LanguageServerHost(Stream inputStream, Stream outputStream, ExportProvide var lspLogger = new LspServiceLogger(_logger); var hostServices = exportProvider.GetExportedValue().HostServices; - _roslynLanguageServer = roslynLspFactory.Create(_jsonRpc, messageFormatter.JsonSerializer, capabilitiesProvider, WellKnownLspServerKinds.CSharpVisualBasicLspServer, lspLogger, hostServices); + _roslynLanguageServer = roslynLspFactory.Create(_jsonRpc, messageFormatter.JsonSerializerOptions, capabilitiesProvider, WellKnownLspServerKinds.CSharpVisualBasicLspServer, lspLogger, hostServices); + } + + internal static SystemTextJsonFormatter CreateJsonMessageFormatter() + { + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddVSCodeInternalExtensionConverters(); + messageFormatter.JsonSerializerOptions.Converters.Add(new NaturalObjectConverter()); + return messageFormatter; } public void Start() diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs new file mode 100644 index 0000000000000..c9e35dd4f0c46 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs @@ -0,0 +1,88 @@ +// 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; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.Options; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer; + +[ExportCSharpVisualBasicLspServiceFactory(typeof(OnInitialized)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class RazorDynamicDocumentSyncRegistration(IGlobalOptionService globalOptionService) : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + => new OnInitialized(globalOptionService); + + public class OnInitialized : IOnInitialized, ILspService + { + private readonly IGlobalOptionService _globalOptionService; + + public OnInitialized(IGlobalOptionService globalOptionService) + { + _globalOptionService = globalOptionService; + } + + public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + { + // Hot reload only works in devkit scenarios. Without, there is no need to register for dynamic document sync. + if (!_globalOptionService.GetOption(LspOptionsStorage.LspUsingDevkitFeatures)) + { + return; + } + + // If dynamic registration for text document synchronization is supported, register for .razor and .cshtml files + // so that they are up to date for hot reload scenarios rather than depending on the file watchers to update + // the contents. + if (clientCapabilities.TextDocument?.Synchronization?.DynamicRegistration is true) + { + var languageServerManager = context.GetRequiredLspService(); + + var documentFilters = new[] { new DocumentFilter() { Pattern = "**/*.{razor, cshtml}", Language = "aspnetcorerazor" } }; + var registrationOptions = new TextDocumentRegistrationOptions() + { + DocumentSelector = documentFilters + }; + + await languageServerManager.SendRequestAsync(Methods.ClientRegisterCapabilityName, + new RegistrationParams() + { + Registrations = [ + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidOpenName, + RegisterOptions = registrationOptions + }, + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidChangeName, + RegisterOptions = new TextDocumentChangeRegistrationOptions() + { + DocumentSelector = documentFilters, + SyncKind = TextDocumentSyncKind.Incremental + } + }, + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidCloseName, + RegisterOptions = registrationOptions + } + ] + }, + cancellationToken).ConfigureAwait(false); + } + } + } +} + diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs index b9b7ea2f7a8b8..f223114d93364 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs @@ -2,7 +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.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -33,9 +33,8 @@ public static async Task ShowToastNotificationAsync(LSP.MessageType messageType, await languageServerManager.SendNotificationAsync(ShowToastNotificationName, toastParams, cancellationToken); } - [DataContract] private record ShowToastNotificationParams( - [property: DataMember(Name = "messageType")] LSP.MessageType MessageType, - [property: DataMember(Name = "message")] string Message, - [property: DataMember(Name = "commands")] LSP.Command[] Commands); + [property: JsonPropertyName("messageType")] LSP.MessageType MessageType, + [property: JsonPropertyName("message")] string Message, + [property: JsonPropertyName("commands")] LSP.Command[] Commands); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj index fa42a2d113562..c3e8ab62d162f 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj @@ -62,7 +62,6 @@ - diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs index 650febe23b4a6..e09710a795c5f 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer; -[DataContract] internal record NamedPipeInformation( - [property: DataMember(Name = "pipeName")] string PipeName); + [property: JsonPropertyName("pipeName")] string PipeName); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs index 5b9b44969f8ab..6baf739265c61 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs @@ -8,6 +8,7 @@ using System.IO.Pipes; using System.Runtime.InteropServices; using System.Runtime.Loader; +using System.Text.Json; using Microsoft.CodeAnalysis.Contracts.Telemetry; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; @@ -18,7 +19,6 @@ using Microsoft.CodeAnalysis.LanguageServer.StarredSuggestions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; -using Newtonsoft.Json; using Roslyn.Utilities; // Setting the title can fail if the process is run without a window, such @@ -120,7 +120,7 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation PipeOptions.CurrentUserOnly | PipeOptions.Asynchronous); // Send the named pipe connection info to the client - Console.WriteLine(JsonConvert.SerializeObject(new NamedPipeInformation(clientPipeName))); + Console.WriteLine(JsonSerializer.Serialize(new NamedPipeInformation(clientPipeName))); // Wait for connection from client await pipeServer.WaitForConnectionAsync(cancellationToken); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs index 4d6774ecd3ab3..db1d59015f096 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs @@ -3,19 +3,19 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using Microsoft.CommonLanguageServerProtocol.Framework.Handlers; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; -internal class ExampleLanguageServer : AbstractLanguageServer +internal class ExampleLanguageServer : SystemTextJsonLanguageServer { private readonly Action? _addExtraHandlers; - public ExampleLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger, Action? addExtraHandlers) : base(jsonRpc, jsonSerializer, logger) + public ExampleLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger, Action? addExtraHandlers) : base(jsonRpc, options, logger) { _addExtraHandlers = addExtraHandlers; // This spins up the queue and ensure the LSP is ready to start receiving requests diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs index 1766f5eeebebc..2a6010a8d4630 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; public class RequestExecutionQueueTests { - private class MockServer : AbstractLanguageServer + private class MockServer : NewtonsoftLanguageServer { public MockServer() : base(new JsonRpc(new HeaderDelimitedMessageHandler(FullDuplexStream.CreatePair().Item1)), JsonSerializer.CreateDefault(), NoOpLspLogger.Instance) { diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs index 8754366f4f30a..f0ae9582cf262 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs @@ -4,12 +4,12 @@ using System; using System.IO; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CommonLanguageServerProtocol.Framework.Example; using Microsoft.Extensions.DependencyInjection; using Nerdbank.Streams; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -19,8 +19,8 @@ internal class TestExampleLanguageServer : ExampleLanguageServer { private readonly JsonRpc _clientRpc; - public TestExampleLanguageServer(Stream clientSteam, JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger, Action? addExtraHandlers) - : base(jsonRpc, jsonSerializer, logger, addExtraHandlers) + public TestExampleLanguageServer(Stream clientSteam, JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger, Action? addExtraHandlers) + : base(jsonRpc, options, logger, addExtraHandlers) { _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientSteam, clientSteam, CreateJsonMessageFormatter())) { @@ -102,10 +102,10 @@ internal async Task WaitForExit() return await _exiting.Task; } - private static JsonMessageFormatter CreateJsonMessageFormatter() + private static SystemTextJsonFormatter CreateJsonMessageFormatter() { - var messageFormatter = new JsonMessageFormatter(); - messageFormatter.JsonSerializer.AddVSInternalExtensionConverters(); + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddVSCodeInternalExtensionConverters(); return messageFormatter; } @@ -121,7 +121,7 @@ internal static TestExampleLanguageServer CreateBadLanguageServer(ILspLogger log serviceCollection.AddSingleton(); }; - var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializer, logger, extraHandlers); + var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializerOptions, logger, extraHandlers); jsonRpc.StartListening(); server.InitializeTest(); @@ -135,7 +135,7 @@ internal static TestExampleLanguageServer CreateLanguageServer(ILspLogger logger var messageFormatter = CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(serverStream, serverStream, messageFormatter)); - var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializer, logger, addExtraHandlers: null); + var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializerOptions, logger, addExtraHandlers: null); jsonRpc.StartListening(); server.InitializeTest(); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs index 8dddabcf31dbe..2a3ca52c14099 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs @@ -12,8 +12,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using StreamJsonRpc; namespace Microsoft.CommonLanguageServerProtocol.Framework; @@ -23,8 +21,6 @@ internal abstract class AbstractLanguageServer private readonly JsonRpc _jsonRpc; protected readonly ILspLogger Logger; - protected readonly JsonSerializer _jsonSerializer; - /// /// These are lazy to allow implementations to define custom variables that are used by /// or @@ -58,12 +54,10 @@ internal abstract class AbstractLanguageServer protected AbstractLanguageServer( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, ILspLogger logger) { Logger = logger; _jsonRpc = jsonRpc; - _jsonSerializer = jsonSerializer; _jsonRpc.AddLocalRpcTarget(this); _jsonRpc.Disconnected += JsonRpc_Disconnected; @@ -102,7 +96,6 @@ protected virtual AbstractHandlerProvider HandlerProvider protected virtual void SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) { - var entryPointMethodInfo = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.ExecuteRequestAsync))!; // Get unique set of methods from the handler provider for the default language. foreach (var methodGroup in handlerProvider .GetRegisteredMethods() @@ -127,13 +120,16 @@ protected virtual void SetupRequestDispatcher(AbstractHandlerProvider handlerPro throw new InvalidOperationException($"Language specific handlers for {methodGroup.Key} have mis-matched number of returns:{Environment.NewLine}{string.Join(Environment.NewLine, methodGroup)}"); } - var delegatingEntryPoint = new DelegatingEntryPoint(methodGroup.Key, this, methodGroup); + var delegatingEntryPoint = CreateDelegatingEntryPoint(methodGroup.Key, methodGroup); var methodAttribute = new JsonRpcMethodAttribute(methodGroup.Key) { UseSingleObjectParameterDeserialization = true, }; - _jsonRpc.AddLocalRpcMethod(entryPointMethodInfo, delegatingEntryPoint, methodAttribute); + // We verified above that parameters match, set flag if this request has parameters or is parameterless so we can set the entrypoint correctly. + var hasParameters = methodGroup.First().RequestType != null; + var entryPoint = delegatingEntryPoint.GetEntryPoint(hasParameters); + _jsonRpc.AddLocalRpcMethod(entryPoint, delegatingEntryPoint, methodAttribute); } static bool AllTypesMatch(IEnumerable types) @@ -178,24 +174,18 @@ protected IRequestExecutionQueue GetRequestExecutionQueue() return _queue.Value; } - protected virtual string GetLanguageForRequest(string methodName, JToken? parameters) - { - Logger.LogInformation($"Using default language handler for {methodName}"); - return LanguageServerConstants.DefaultLanguageName; - } + protected abstract DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod); - private sealed class DelegatingEntryPoint + protected abstract class DelegatingEntryPoint { - private readonly string _method; - private readonly Lazy> _languageEntryPoint; - private readonly AbstractLanguageServer _target; + protected readonly string _method; + protected readonly Lazy> _languageEntryPoint; private static readonly MethodInfo s_queueExecuteAsyncMethod = typeof(RequestExecutionQueue).GetMethod(nameof(RequestExecutionQueue.ExecuteAsync))!; - public DelegatingEntryPoint(string method, AbstractLanguageServer target, IGrouping handlersForMethod) + public DelegatingEntryPoint(string method, IGrouping handlersForMethod) { _method = method; - _target = target; _languageEntryPoint = new Lazy>(() => { var handlerEntryPoints = new Dictionary(); @@ -211,61 +201,40 @@ public DelegatingEntryPoint(string method, AbstractLanguageServer - /// StreamJsonRpc entry point for all handler methods. - /// The optional parameters allow StreamJsonRpc to call into the same method for any kind of request / notification (with any number of params or response). - /// - public async Task ExecuteRequestAsync(JToken? request = null, CancellationToken cancellationToken = default) - { - var queue = _target.GetRequestExecutionQueue(); - var lspServices = _target.GetLspServices(); - - // Retrieve the language of the request so we know how to deserialize it. - var language = _target.GetLanguageForRequest(_method, request); + public abstract MethodInfo GetEntryPoint(bool hasParameter); - // Find the correct request and response types for the given request and language. + protected (MethodInfo MethodInfo, RequestHandlerMetadata Metadata) GetMethodInfo(string language) + { if (!_languageEntryPoint.Value.TryGetValue(language, out var requestInfo) && !_languageEntryPoint.Value.TryGetValue(LanguageServerConstants.DefaultLanguageName, out requestInfo)) { throw new InvalidOperationException($"No default or language specific handler was found for {_method} and document with language {language}"); } - // Deserialize the request parameters (if any). - var requestObject = DeserializeRequest(request, requestInfo.Metadata, _target._jsonSerializer); + return requestInfo; + } - var task = requestInfo.MethodInfo.Invoke(queue, [requestObject, _method, language, lspServices, cancellationToken]) as Task + protected async Task InvokeAsync( + MethodInfo methodInfo, + IRequestExecutionQueue queue, + object? requestObject, + string language, + ILspServices lspServices, + CancellationToken cancellationToken) + { + var task = methodInfo.Invoke(queue, [requestObject, _method, language, lspServices, cancellationToken]) as Task ?? throw new InvalidOperationException($"Queue result task cannot be null"); await task.ConfigureAwait(false); var resultProperty = task.GetType().GetProperty("Result") ?? throw new InvalidOperationException("Result property on task cannot be null"); var result = resultProperty.GetValue(task); - if (result is null || result == NoValue.Instance) + if (result == NoValue.Instance) { return null; } - - return JToken.FromObject(result, _target._jsonSerializer); - } - - private static object DeserializeRequest(JToken? request, RequestHandlerMetadata metadata, JsonSerializer jsonSerializer) - { - if (request is null && metadata.RequestType is not null) - { - throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); - } - - if (request is not null && metadata.RequestType is null) + else { - throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + return result; } - - object requestObject = NoValue.Instance; - if (request is not null) - { - requestObject = request.ToObject(metadata.RequestType, jsonSerializer) - ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); - } - - return requestObject; } } diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs index a7bb1e13ac058..9f2a608ea16fc 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs @@ -6,7 +6,6 @@ #nullable enable using System; -using System.Linq; namespace Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems index 4f299de12be5e..58e64162145c4 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems @@ -30,10 +30,12 @@ + + \ No newline at end of file diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs new file mode 100644 index 0000000000000..8af14dc358410 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs @@ -0,0 +1,102 @@ +// 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. + +// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable +#nullable enable + +using System; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Basic implementation of using Newtonsoft for serialization. +/// +internal abstract class NewtonsoftLanguageServer : AbstractLanguageServer +{ + private readonly JsonSerializer _jsonSerializer; + protected NewtonsoftLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger) : base(jsonRpc, logger) + { + _jsonSerializer = jsonSerializer; + } + + protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod) + { + return new NewtonsoftDelegatingEntryPoint(method, handlersForMethod, this); + } + + protected virtual string GetLanguageForRequest(string methodName, JToken? parameters) + { + Logger.LogInformation($"Using default language handler for {methodName}"); + return LanguageServerConstants.DefaultLanguageName; + } + + private class NewtonsoftDelegatingEntryPoint( + string method, + IGrouping handlersForMethod, + NewtonsoftLanguageServer target) : DelegatingEntryPoint(method, handlersForMethod) + { + private static readonly MethodInfo s_entryPoint = typeof(NewtonsoftDelegatingEntryPoint).GetMethod(nameof(NewtonsoftDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!; + + public override MethodInfo GetEntryPoint(bool hasParameter) + { + return s_entryPoint; + } + + /// + /// StreamJsonRpc entry point for all handler methods. + /// The optional parameters allow StreamJsonRpc to call into the same method for any kind of request / notification (with any number of params or response). + /// + private async Task ExecuteRequestAsync(JToken? request = null, CancellationToken cancellationToken = default) + { + var queue = target.GetRequestExecutionQueue(); + var lspServices = target.GetLspServices(); + + // Retrieve the language of the request so we know how to deserialize it. + var language = target.GetLanguageForRequest(_method, request); + + // Find the correct request and response types for the given request and language. + var requestInfo = GetMethodInfo(language); + + // Deserialize the request parameters (if any). + var requestObject = DeserializeRequest(request, requestInfo.Metadata, target._jsonSerializer); + + var result = await InvokeAsync(requestInfo.MethodInfo, queue, requestObject, language, lspServices, cancellationToken).ConfigureAwait(false); + if (result is null) + { + return null; + } + + return JToken.FromObject(result, target._jsonSerializer); + } + + private static object DeserializeRequest(JToken? request, RequestHandlerMetadata metadata, JsonSerializer jsonSerializer) + { + if (request is null && metadata.RequestType is not null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); + } + + if (request is not null && metadata.RequestType is null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + } + + object requestObject = NoValue.Instance; + if (request is not null) + { + requestObject = request.ToObject(metadata.RequestType, jsonSerializer) + ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); + } + + return requestObject; + } + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index cd200a1ca0ec7..60f3bf3271c00 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -12,7 +12,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Threading; -using Newtonsoft.Json.Linq; namespace Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs new file mode 100644 index 0000000000000..57cebae1a4ecf --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs @@ -0,0 +1,107 @@ +// 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. + +// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable +#nullable enable + +using System; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +internal abstract class SystemTextJsonLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger) : AbstractLanguageServer(jsonRpc, logger) +{ + /// + /// JsonSerializer options used by streamjsonrpc (and for serializing / deserializing the requests to streamjsonrpc). + /// These options are specifically from the that added the exotic type converters. + /// + private readonly JsonSerializerOptions _jsonSerializerOptions = options; + protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod) + { + return new SystemTextJsonDelegatingEntryPoint(method, handlersForMethod, this); + } + + protected virtual string GetLanguageForRequest(string methodName, JsonElement? parameters) + { + Logger.LogInformation($"Using default language handler for {methodName}"); + return LanguageServerConstants.DefaultLanguageName; + } + + private sealed class SystemTextJsonDelegatingEntryPoint( + string method, + IGrouping handlersForMethod, + SystemTextJsonLanguageServer target) : DelegatingEntryPoint(method, handlersForMethod) + { + private static readonly MethodInfo s_parameterlessEntryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequest0Async), BindingFlags.NonPublic | BindingFlags.Instance)!; + private static readonly MethodInfo s_entryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!; + + public override MethodInfo GetEntryPoint(bool hasParameter) + { + return hasParameter ? s_entryPoint : s_parameterlessEntryPoint; + } + + /// + /// StreamJsonRpc entry point for handlers with no parameters. + /// Unlike Newtonsoft, we have to differentiate instead of using default parameters. + /// + private Task ExecuteRequest0Async(CancellationToken cancellationToken = default) + { + return ExecuteRequestAsync(null, cancellationToken); + } + + /// + /// StreamJsonRpc entry point for handlers with parameters (and any response) type. + /// + private async Task ExecuteRequestAsync(JsonElement? request, CancellationToken cancellationToken = default) + { + var queue = target.GetRequestExecutionQueue(); + var lspServices = target.GetLspServices(); + + // Retrieve the language of the request so we know how to deserialize it. + var language = target.GetLanguageForRequest(_method, request); + + // Find the correct request and response types for the given request and language. + var requestInfo = GetMethodInfo(language); + + // Deserialize the request parameters (if any). + var requestObject = DeserializeRequest(request, requestInfo.Metadata, target._jsonSerializerOptions); + + var result = await InvokeAsync(requestInfo.MethodInfo, queue, requestObject, language, lspServices, cancellationToken).ConfigureAwait(false); + if (result is null) + { + return null; + } + + var serializedResult = JsonSerializer.SerializeToElement(result, target._jsonSerializerOptions); + return serializedResult; + } + + private static object DeserializeRequest(JsonElement? request, RequestHandlerMetadata metadata, JsonSerializerOptions options) + { + if (request is null && metadata.RequestType is not null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); + } + + if (request is not null && metadata.RequestType is null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + } + + object requestObject = NoValue.Instance; + if (request is not null) + { + requestObject = JsonSerializer.Deserialize(request.Value, metadata.RequestType!, options) + ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); + } + + return requestObject; + } + } +} diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs index 5e2276385b282..1239d78edbc73 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs @@ -3,14 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; -using System.IO; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer @@ -30,7 +28,7 @@ public CSharpVisualBasicLanguageServerFactory( public AbstractLanguageServer Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, @@ -39,7 +37,7 @@ public AbstractLanguageServer Create( var server = new RoslynLanguageServer( _lspServiceProvider, jsonRpc, - jsonSerializer, + options, capabilitiesProvider, logger, hostServices, diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs index b72b3e00097fa..03bf62eacb2e4 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.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; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index b46fbf4e76b96..9b18f67090ed1 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -106,6 +107,20 @@ internal static partial class ProtocolConversions { WellKnownTags.Deprecated, ImmutableArray.Create(LSP.CompletionItemTag.Deprecated) }, }.ToImmutableDictionary(); + public static JsonSerializerOptions AddLspSerializerOptions(this JsonSerializerOptions options) + { + LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(options); + options.Converters.Add(new NaturalObjectConverter()); + return options; + } + + /// + /// Options that know how to serialize / deserialize basic LSP types. + /// Useful when there are particular fields that are not serialized or deserialized by normal request handling (for example + /// deserializing a field that is typed as object instead of a concrete type). + /// + public static JsonSerializerOptions LspJsonSerializerOptions = new JsonSerializerOptions().AddLspSerializerOptions(); + // TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726 public static async Task LSPToRoslynCompletionTriggerAsync( diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs index f513d42e385b6..74ced5c517c6f 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs @@ -2,20 +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.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; -[DataContract] internal sealed record FormatNewFileParams { - [DataMember(Name = "document")] + [JsonPropertyName("document")] public required TextDocumentIdentifier Document { get; init; } - [DataMember(Name = "project")] + [JsonPropertyName("project")] public required TextDocumentIdentifier Project { get; init; } - [DataMember(Name = "contents")] + [JsonPropertyName("contents")] public required string Contents { get; init; } } diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs index 1e5d1fc0f4bb7..059f1e7402c87 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs @@ -3,13 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; -[DataContract] internal class SemanticTokensRangesParams : SemanticTokensParams { - [DataMember(Name = "ranges")] + [JsonPropertyName("ranges")] public required Range[] Ranges { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs index ba7495c8f93a9..a36a303b94258 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs @@ -2,7 +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.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; @@ -11,18 +11,17 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; // Summary: // Class representing the parameters sent from the client to the server for the // roslyn/simplifyMethod request. -[DataContract] internal record SimplifyMethodParams : ITextDocumentParams { // // Summary: // Gets or sets the value which identifies the document where the text edit will be placed. - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public required TextDocumentIdentifier TextDocument { get; set; } // // Summary: // Gets or sets the value which identifies the text edit to be simplified. - [DataMember(Name = "textEdit")] + [JsonPropertyName("textEdit")] public required TextEdit TextEdit { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 6e8e167de9c82..6348b6ae6be5e 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 1d82aa9383dad..9d82ea0fa633e 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -2,10 +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.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs index fde0bc07d9891..b2c78656b3497 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs @@ -2,9 +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.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CodeFixes diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 52f031d5db3a5..61b85eb647bab 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -141,8 +141,8 @@ public async Task GetMostSevereFixAsync( } } - var errorFixTask = Task.Run(() => GetFirstFixAsync(spanToErrorDiagnostics, cancellationToken), cancellationToken); - var otherFixTask = Task.Run(() => GetFirstFixAsync(spanToOtherDiagnostics, linkedToken), linkedToken); + var errorFixTask = GetFirstFixAsync(spanToErrorDiagnostics, cancellationToken); + var otherFixTask = GetFirstFixAsync(spanToOtherDiagnostics, linkedToken); // If the error diagnostics task happens to complete with a non-null result before // the other diagnostics task, we can cancel the other task. @@ -156,6 +156,9 @@ public async Task GetMostSevereFixAsync( SortedDictionary> spanToDiagnostics, CancellationToken cancellationToken) { + // Ensure we yield here so the caller can continue on. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, priorityProvider, fallbackOptions, _ => null, cancellationToken).ConfigureAwait(false)) diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs index e0c55f84e1acf..609d36eb9ee08 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeFixes diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index be32623daf749..b7841f2bc4d43 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics @@ -68,7 +69,7 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void RequestDiagnosticRefresh() => _diagnosticsRefresher?.RequestWorkspaceRefresh(); - public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( + public async Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, @@ -81,20 +82,18 @@ public void RequestDiagnosticRefresh() var analyzer = CreateIncrementalAnalyzer(document.Project.Solution.Workspace); // always make sure that analyzer is called on background thread. - return Task.Run(async () => - { - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - - using var _ = ArrayBuilder.GetInstance(out var diagnostics); - var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( - document, range, diagnostics, shouldIncludeDiagnostic, - includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, - addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); - return (diagnostics.ToImmutable(), upToDate); - }, cancellationToken); + await TaskScheduler.Default; + priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); + + using var _ = ArrayBuilder.GetInstance(out var diagnostics); + var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( + document, range, diagnostics, shouldIncludeDiagnostic, + includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, + addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + return (diagnostics.ToImmutable(), upToDate); } - public Task> GetDiagnosticsForSpanAsync( + public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, @@ -110,9 +109,10 @@ public Task> GetDiagnosticsForSpanAsync( priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); // always make sure that analyzer is called on background thread. - return Task.Run(() => analyzer.GetDiagnosticsForSpanAsync( + await TaskScheduler.Default; + return await analyzer.GetDiagnosticsForSpanAsync( document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); + priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); } public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs index 321e5cb7ee1f2..d5de93548ef16 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 0afc2829fd4ba..3be6f73e60c14 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index fd15d29e0bff6..4abe7eb44d1e0 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.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.Linq; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 172fb7357b49e..1f45073d32495 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.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; using System.Collections.Immutable; using System.Diagnostics; using System.Threading; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index fe01283879f67..6e0b2fc05ab45 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -7,10 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 9a2f24933bc04..c7e696eb8d421 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 1ddadcfbd2bf1..c42de66646961 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index f24820a50f801..6675a828608cc 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -5,10 +5,10 @@ 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.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -24,51 +24,33 @@ public Task> GetDiagnosticsForIdsAsync(Solution s public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); - private abstract class DiagnosticGetter + private abstract class DiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + Func>? getDocuments, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) { - protected readonly DiagnosticIncrementalAnalyzer Owner; - - protected readonly Solution Solution; - protected readonly ProjectId? ProjectId; - protected readonly DocumentId? DocumentId; - protected readonly bool IncludeSuppressedDiagnostics; - protected readonly bool IncludeLocalDocumentDiagnostics; - protected readonly bool IncludeNonLocalDocumentDiagnostics; - - private readonly Func> _getDocuments; - - private ImmutableArray.Builder? _lazyDataBuilder; - - public DiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, - Solution solution, - ProjectId? projectId, - DocumentId? documentId, - Func>? getDocuments, - bool includeSuppressedDiagnostics, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) - { - Owner = owner; - Solution = solution; + protected readonly DiagnosticIncrementalAnalyzer Owner = owner; - DocumentId = documentId; - _getDocuments = getDocuments ?? (static (project, documentId) => documentId != null ? [documentId] : project.DocumentIds); - ProjectId = projectId ?? documentId?.ProjectId; + protected readonly Solution Solution = solution; + protected readonly ProjectId? ProjectId = projectId ?? documentId?.ProjectId; + protected readonly DocumentId? DocumentId = documentId; + protected readonly bool IncludeSuppressedDiagnostics = includeSuppressedDiagnostics; + protected readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; + protected readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - IncludeSuppressedDiagnostics = includeSuppressedDiagnostics; - IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; - IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - } + private readonly Func> _getDocuments = getDocuments ?? (static (project, documentId) => documentId != null ? [documentId] : project.DocumentIds); protected StateManager StateManager => Owner._stateManager; protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; - protected ImmutableArray GetDiagnosticData() - => (_lazyDataBuilder != null) ? _lazyDataBuilder.ToImmutableArray() : []; - - protected abstract Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken); + protected abstract Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, Action callback, CancellationToken cancellationToken); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { @@ -76,53 +58,40 @@ public async Task> GetDiagnosticsAsync(Cancellati { var project = Solution.GetProject(ProjectId); if (project == null) - { - return GetDiagnosticData(); - } - - var documentIds = _getDocuments(project, DocumentId); + return []; // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; - await AppendDiagnosticsAsync(project, documentIds, includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); - return GetDiagnosticData(); + return await ProduceProjectDiagnosticsAsync( + [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } - await AppendDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); - return GetDiagnosticData(); + return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); } - protected async Task AppendDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) + protected Task> ProduceSolutionDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) + => ProduceProjectDiagnosticsAsync(solution.Projects, static project => project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken); + + protected async Task> ProduceProjectDiagnosticsAsync( + IEnumerable projects, Func> getDocumentIds, + bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. - var includeProjectNonLocalResult = true; - - var tasks = new Task[solution.ProjectIds.Count]; - var index = 0; - foreach (var project in solution.Projects) - { - var localProject = project; - tasks[index++] = Task.Run( - () => AppendDiagnosticsAsync( - localProject, localProject.DocumentIds, includeProjectNonLocalResult, cancellationToken), cancellationToken); - } - - await Task.WhenAll(tasks).ConfigureAwait(false); + return await ProducerConsumer.RunParallelAsync( + source: projects, + produceItems: static (project, callback, args, cancellationToken) => args.@this.ProduceDiagnosticsAsync( + project, args.getDocumentIds(project), args.includeProjectNonLocalResult, callback, cancellationToken), + args: (@this: this, getDocumentIds, includeProjectNonLocalResult), + cancellationToken).ConfigureAwait(false); } - protected void AppendDiagnostics(ImmutableArray items) + protected void InvokeCallback(Action callback, ImmutableArray diagnostics) { - Debug.Assert(!items.IsDefault); - - if (_lazyDataBuilder == null) - { - Interlocked.CompareExchange(ref _lazyDataBuilder, ImmutableArray.CreateBuilder(), null); - } - - lock (_lazyDataBuilder) + foreach (var diagnostic in diagnostics) { - _lazyDataBuilder.AddRange(items.Where(ShouldIncludeSuppressedDiagnostic).Where(ShouldIncludeDiagnostic)); + if (ShouldIncludeSuppressedDiagnostic(diagnostic) && ShouldIncludeDiagnostic(diagnostic)) + callback(diagnostic); } } @@ -130,14 +99,19 @@ private bool ShouldIncludeSuppressedDiagnostic(DiagnosticData diagnostic) => IncludeSuppressedDiagnostics || !diagnostic.IsSuppressed; } - private sealed class IdeCachedDiagnosticGetter : DiagnosticGetter + private sealed class IdeCachedDiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( + owner, solution, projectId, documentId, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { - public IdeCachedDiagnosticGetter(DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) - : base(owner, solution, projectId, documentId, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) - { - } - - protected override async Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, + Action callback, CancellationToken cancellationToken) { foreach (var stateSet in StateManager.GetStateSets(project.Id)) { @@ -145,18 +119,18 @@ protected override async Task AppendDiagnosticsAsync(Project project, IEnumerabl { if (IncludeLocalDocumentDiagnostics) { - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); } if (IncludeNonLocalDocumentDiagnostics) - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - AppendDiagnostics(await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } } } @@ -225,43 +199,43 @@ private static async Task> GetProjectStateDiagnos } } - private sealed class IdeLatestDiagnosticGetter : DiagnosticGetter + private sealed class IdeLatestDiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + ImmutableHashSet? diagnosticIds, + Func? shouldIncludeAnalyzer, + Func>? getDocuments, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( + owner, solution, projectId, documentId, getDocuments, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { - private readonly ImmutableHashSet? _diagnosticIds; - private readonly Func? _shouldIncludeAnalyzer; - - public IdeLatestDiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, - DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, - Func>? getDocuments, - bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) - : base(owner, solution, projectId, documentId, getDocuments, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) - { - _diagnosticIds = diagnosticIds; - _shouldIncludeAnalyzer = shouldIncludeAnalyzer; - } + private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; + private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { if (ProjectId != null) { var project = Solution.GetProject(ProjectId); - if (project != null) - { - await AppendDiagnosticsAsync(project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); - } + if (project is null) + return []; - return GetDiagnosticData(); + return await ProduceProjectDiagnosticsAsync( + [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } - await AppendDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); - return GetDiagnosticData(); + return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); - protected override async Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, + Action callback, CancellationToken cancellationToken) { // get analyzers that are not suppressed. var stateSets = StateManager.GetOrCreateStateSets(project).Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); @@ -281,18 +255,18 @@ protected override async Task AppendDiagnosticsAsync(Project project, IEnumerabl { if (IncludeLocalDocumentDiagnostics) { - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); } if (IncludeNonLocalDocumentDiagnostics) - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - AppendDiagnostics(analysisResult.GetOtherDiagnostics()); + InvokeCallback(callback, analysisResult.GetOtherDiagnostics()); } } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs index 0e9dfb571229b..dd86515b9604f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs @@ -2,20 +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.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer diff --git a/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs b/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs index 9a607836aa66c..955b3b7eb142d 100644 --- a/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs +++ b/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Notification; namespace Microsoft.CodeAnalysis.FindUsages { diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs index bb07b3685be4a..50fd4952d3793 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs @@ -3,7 +3,6 @@ // 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; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs index 514a457c471a0..ef07ceee6afd4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.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.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs b/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs index efa334e23c319..5217e63350ad3 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.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 System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs index 3b6ffa92508a1..d89464da63ae2 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs index 3d6fc0a911d31..6fe985eff906c 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs index 11f6993cb7301..c6806df2a42b2 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; namespace Microsoft.CodeAnalysis.UnifiedSuggestions diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs index a0c207fefe169..1e85c481f5a77 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.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; using Microsoft.CodeAnalysis.CodeActions; namespace Microsoft.CodeAnalysis.UnifiedSuggestions diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index cf1b8f407a589..b37ddde7869e0 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; using CodeFixGroupKey = System.Tuple; @@ -43,15 +44,16 @@ public static async ValueTask> GetFilt { var originalSolution = document.Project.Solution; - // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally - // run any of this on the UI thread and potentially allow any code to take a dependency on that. - var fixes = await Task.Run(() => codeFixService.GetFixesAsync( + // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally run any of + // this on the UI thread and potentially allow any code to take a dependency on that. + await TaskScheduler.Default; + var fixes = await codeFixService.GetFixesAsync( document, selection, priorityProvider, fallbackOptions, addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); @@ -443,14 +445,12 @@ public static async Task> GetFilterAnd bool filterOutsideSelection, CancellationToken cancellationToken) { - // It may seem strange that we kick off a task, but then immediately 'Wait' on - // it. However, it's deliberate. We want to make sure that the code runs on - // the background so that no one takes an accidentally dependency on running on - // the UI thread. - var refactorings = await Task.Run( - () => codeRefactoringService.GetRefactoringsAsync( - document, selection, priority, options, addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); + // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally run any of + // this on the UI thread and potentially allow any code to take a dependency on that. + await TaskScheduler.Default; + var refactorings = await codeRefactoringService.GetRefactoringsAsync( + document, selection, priority, options, addOperationScope, + cancellationToken).ConfigureAwait(false); var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs index aad78be589aa2..103dd05fba686 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -11,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; @@ -35,12 +35,13 @@ internal sealed class CodeActionFixAllResolveHandler( public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(RoslynFixAllCodeAction request) - => ((JToken)request.Data!).ToObject()!.TextDocument; + => GetCodeActionResolveData(request).TextDocument; public async Task HandleRequestAsync(RoslynFixAllCodeAction request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - var data = ((JToken)request.Data!).ToObject(); + Contract.ThrowIfNull(request.Data); + var data = GetCodeActionResolveData(request); Assumes.Present(data); var options = _globalOptions.GetCodeActionOptionsProvider(); @@ -65,4 +66,12 @@ public async Task HandleRequestAsync(RoslynFixAllCodeAct request.Edit = edit; return request; } + + private static CodeActionResolveData GetCodeActionResolveData(RoslynFixAllCodeAction request) + { + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); + Contract.ThrowIfNull(resolveData, "Missing data for fix all code action resolve request"); + return resolveData; + + } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 44136ac1f6479..0b8ff43d64255 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.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; @@ -16,7 +15,6 @@ using Microsoft.CodeAnalysis.UnifiedSuggestions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; -using StreamJsonRpc; using CodeAction = Microsoft.CodeAnalysis.CodeActions.CodeAction; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs index d7e32c9199d8e..ce2f356f4730a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions @@ -33,10 +33,10 @@ internal class CodeActionResolveData public string[] CodeActionPath { get; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? FixAllFlavors { get; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImmutableArray? NestedCodeActions { get; } public CodeActionResolveData( diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index 064010e74e7f5..26710c2b6ebb0 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -12,9 +13,9 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.Options; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; +using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -52,11 +53,12 @@ public CodeActionResolveHandler( public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) - => ((JToken)request.Data!).ToObject()!.TextDocument; + => GetCodeActionResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.CodeAction codeAction, RequestContext context, CancellationToken cancellationToken) { - var data = ((JToken)codeAction.Data!).ToObject(); + Contract.ThrowIfNull(codeAction.Data); + var data = GetCodeActionResolveData(codeAction); Assumes.Present(data); // Fix All Code Action does not need further resolution since it already has the command callback @@ -97,4 +99,11 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) codeAction.Edit = edit; return codeAction; } + + private static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) + { + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); + Contract.ThrowIfNull(resolveData, "Missing data for code action resolve request"); + return resolveData; + } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs index 0e0ac5a264fec..c13a4e7e1d649 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs @@ -11,10 +11,8 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Options; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs index 03fdfc48564c3..2d343c92d434e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs @@ -2,15 +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.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -[DataContract] internal sealed class RoslynFixAllCodeAction(string scope) : CodeAction { - [JsonProperty(PropertyName = "scope")] + [JsonPropertyName("scope")] public string Scope { get; } = scope; } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs index 9bb72371dbfa3..116e30bea6158 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; @@ -18,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using LSP = Roslyn.LanguageServer.Protocol; using Microsoft.CodeAnalysis.Text; -using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index 930bf05308462..5085dcd4ad670 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -2,15 +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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; -using StreamJsonRpc; using Microsoft.CodeAnalysis.Shared.Extensions; using LSP = Roslyn.LanguageServer.Protocol; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; @@ -83,7 +82,8 @@ public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeLens request private static CodeLensResolveData GetCodeLensResolveData(LSP.CodeLens codeLens) { - var resolveData = (codeLens.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(codeLens.Data); + var resolveData = JsonSerializer.Deserialize((JsonElement)codeLens.Data, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for code lens resolve request"); return resolveData; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 4a8d828e9ac97..5a17d02cd577f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -73,8 +73,9 @@ public CompletionHandler( var (list, isIncomplete, resultId) = completionListResult.Value; var creationService = document.Project.Solution.Services.GetRequiredService(); - return await creationService.ConvertToLspCompletionListAsync(document, position, capabilityHelper, list, isIncomplete, resultId, cancellationToken) + var result = await creationService.ConvertToLspCompletionListAsync(document, position, capabilityHelper, list, isIncomplete, resultId, cancellationToken) .ConfigureAwait(false); + return result; } private async Task<(CompletionList CompletionList, bool IsIncomplete, long ResultId)?> GetFilteredCompletionListAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs index eae62bc03704a..4c739a05c89e0 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.Completion; using static Microsoft.CodeAnalysis.LanguageServer.Handler.Completion.CompletionListCache; -using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion { diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs index 75c3eec604eb8..31f932e9d66e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; @@ -11,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -90,7 +90,7 @@ private static bool MatchesLSPCompletionItem(LSP.CompletionItem lspCompletionIte private static LSP.TextDocumentIdentifier? GetTextDocumentCacheEntry(LSP.CompletionItem request) { Contract.ThrowIfNull(request.Data); - var resolveData = ((JToken)request.Data).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data); if (resolveData is null) { Contract.Fail("Document should always be provided when resolving a completion item request."); @@ -103,7 +103,7 @@ private static bool MatchesLSPCompletionItem(LSP.CompletionItem lspCompletionIte private CompletionListCache.CacheEntry? GetCompletionListCacheEntry(LSP.CompletionItem request) { Contract.ThrowIfNull(request.Data); - var resolveData = ((JToken)request.Data).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data, ProtocolConversions.LspJsonSerializerOptions); if (resolveData?.ResultId == null) { Contract.Fail("Result id should always be provided when resolving a completion item we returned."); diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs index 755d8e8f90813..5fec8b6b20c28 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Text; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion diff --git a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs index c61ec8ca969a1..d414b90d5cc92 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ImplementType; @@ -12,7 +14,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -114,17 +115,18 @@ private void SetOption(IOption2 option, string valueFromClient, string? language } } - private async Task> GetConfigurationsAsync(CancellationToken cancellationToken) + private async Task> GetConfigurationsAsync(CancellationToken cancellationToken) { try { var configurationParams = new ConfigurationParams() { Items = _configurationItems.AsArray() }; - var options = await _clientLanguageServerManager.SendRequestAsync( + var options = await _clientLanguageServerManager.SendRequestAsync( Methods.WorkspaceConfigurationName, configurationParams, cancellationToken).ConfigureAwait(false); // Failed to get result from client. Contract.ThrowIfNull(options); - return options.SelectAsArray(token => token.ToString()); + var converted = options.SelectAsArray(token => token?.ToString()); + return converted; } catch (Exception e) { diff --git a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs index f1e2f16defb48..19acbf8650cc1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.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.Text.Json; using System.Threading; using System.Threading.Tasks; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Configuration { @@ -15,7 +15,7 @@ public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, Requ { if (clientCapabilities?.Workspace?.DidChangeConfiguration?.DynamicRegistration is true) { - await _clientLanguageServerManager.SendRequestAsync( + await _clientLanguageServerManager.SendRequestAsync( methodName: Methods.ClientRegisterCapabilityName, @params: new RegistrationParams() { diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs index 83948cec1e616..9734dafe3a332 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs @@ -2,12 +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.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.GoToDefinition; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 94bb3f537e1c9..b6fd1092b555d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -411,8 +410,11 @@ LSP.VSDiagnostic CreateLspDiagnostic( if (capabilities.HasVisualStudioLspCapability()) { + // The client expects us to return null if there is no message (not an empty string). + var expandedMessage = string.IsNullOrEmpty(diagnosticData.Description) ? null : diagnosticData.Description; + diagnostic.DiagnosticType = diagnosticData.Category; - diagnostic.ExpandedMessage = diagnosticData.Description; + diagnostic.ExpandedMessage = expandedMessage; diagnostic.Projects = [ new VSDiagnosticProjectInformation diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs index 7e3d6e4619791..ab6ada210db7c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs @@ -9,6 +9,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSources; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.TaskList; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs index f3ac503169a17..1b30a4622f72d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs @@ -7,7 +7,7 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] -internal record class BuildOnlyDiagnosticIdsResult([property: DataMember(Name = "ids")] string[] Ids); +internal record class BuildOnlyDiagnosticIdsResult([property: JsonPropertyName("ids")] string[] Ids); [ExportCSharpVisualBasicStatelessLspService(typeof(BuildOnlyDiagnosticIdsHandler)), Shared] [Method(BuildOnlyDiagnosticIdsMethodName)] diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index 16ee2f87d2bf2..050cfe582e16e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -3,6 +3,8 @@ // 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.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSources; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs index 0384599ba952f..2a0b22abfaf86 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.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; - namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal static class PullDiagnosticConstants diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index 54a811861cab1..b75b6c9793e95 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges diff --git a/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs b/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs index bc2a111664bc3..3984c475b0542 100644 --- a/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs @@ -5,6 +5,7 @@ using System; using System.Composition; using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EditAndContinue; @@ -13,8 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.EditAndContinue; -[DataContract] -internal readonly record struct LspSolutionSnapshotId([property: DataMember(Name = "id")] int Id); +internal readonly record struct LspSolutionSnapshotId([property: JsonPropertyName("id")] int Id); [ExportCSharpVisualBasicStatelessLspService(typeof(RegisterSolutionSnapshotHandler)), Shared] [Method("workspace/_vs_registerSolutionSnapshot")] diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index 806e79597b426..c9b1712f9d66a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler @@ -42,7 +41,7 @@ internal abstract class AbstractFormatDocumentHandlerBase(); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 0c60cfe6e1fa8..a3762bb7fe1ad 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -3,8 +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.Composition; using System.Linq; using System.Threading; @@ -15,9 +13,7 @@ using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { diff --git a/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs b/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs index 275c707cdb142..ff2410d5277d7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs +++ b/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs @@ -5,10 +5,8 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; using Microsoft.CodeAnalysis.Text; -using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs index 44c6877e471a6..c15f24183032d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.InlineHints; -using Roslyn.LanguageServer.Protocol; using static Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint.InlayHintCache; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 8da7e981f854a..08025830a4bca 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; @@ -14,7 +13,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs index 232a9858c26ac..361bbc4f432e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs @@ -5,7 +5,6 @@ using System; using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 75cf236aee41c..d1b6b6a966ab4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -3,18 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.Drawing; -using System.Reflection.Metadata; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.InlineHints; -using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint { @@ -33,7 +29,7 @@ public InlayHintResolveHandler(InlayHintCache inlayHintCache) public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) - => GetTextDocument(request.Data) ?? throw new ArgumentException(); + => GetInlayHintResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { @@ -58,13 +54,6 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) return request; } - private static LSP.TextDocumentIdentifier? GetTextDocument(object? requestData) - { - Contract.ThrowIfNull(requestData); - var resolveData = ((JToken)requestData).ToObject(); - return resolveData?.TextDocument; - } - private (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData) { var cacheEntry = _inlayHintCache.GetCachedEntry(resolveData.ResultId); @@ -74,7 +63,8 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) private static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) { - var resolveData = (inlayHint.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(inlayHint.Data); + var resolveData = JsonSerializer.Deserialize((JsonElement)inlayHint.Data, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for inlay hint resolve request"); return resolveData; } diff --git a/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs b/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs index 8a4c55e4ca7bb..af0a848d1d9ac 100644 --- a/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs +++ b/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs @@ -2,10 +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; - namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// diff --git a/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs b/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs index c4fdd4c88e8d2..c9ca5e63f60ef 100644 --- a/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs +++ b/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs index 8e24d9425f109..bdfdaf086746d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs index 80e447be3956f..343299bec39e4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs index 9e57963e23ef3..c562f50912455 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs index f0610290edbde..a9426705cc246 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs index 7341d04b9c5d9..1ff18f536c1e3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs @@ -36,8 +36,6 @@ public RequestTelemetryLogger(string serverTypeName) _requestCounters = new(); _findDocumentResults = new(); _usedForkedSolutionCounter = new(); - - TelemetryLogging.Flushed += OnFlushed; } public void UpdateFindDocumentTelemetryData(bool success, string? workspaceKind) @@ -94,14 +92,6 @@ public void Dispose() return; } - // Flush all telemetry logged through TelemetryLogging - TelemetryLogging.Flush(); - - TelemetryLogging.Flushed -= OnFlushed; - } - - private void OnFlushed(object? sender, EventArgs e) - { foreach (var kvp in _requestCounters) { TelemetryLogging.Log(FunctionId.LSP_RequestCounter, KeyValueLogMessage.Create(LogType.Trace, m => @@ -134,6 +124,9 @@ private void OnFlushed(object? sender, EventArgs e) } })); + // Flush all telemetry logged through TelemetryLogging + TelemetryLogging.Flush(); + _requestCounters.Clear(); } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index 899203510b20d..4ee36deb369e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.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; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs index 94d4759c38bc1..ea9cd0fe5dea6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -41,7 +41,7 @@ public Task HandleRequestAsync(InitializeParams request, Reque Logger.Log(FunctionId.LSP_Initialize, KeyValueLogMessage.Create(m => { m["serverKind"] = context.ServerKind.ToTelemetryString(); - m["capabilities"] = JsonConvert.SerializeObject(serverCapabilities); + m["capabilities"] = JsonSerializer.Serialize(serverCapabilities, ProtocolConversions.LspJsonSerializerOptions); })); return Task.FromResult(new InitializeResult diff --git a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs index 541ffc8888b7b..b294d4998aa67 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.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; using System.Collections.Immutable; using System.Threading; using Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs index a508b0251c4b8..d71fe27838375 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs @@ -2,9 +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.Runtime.Serialization; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -15,13 +14,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal sealed class RoslynDocumentSymbol : DocumentSymbol { - [DataMember(IsRequired = false, Name = "glyph")] + [JsonPropertyName("glyph")] public int Glyph { get; set; } // Deliberately override the value in the base so that our serializers/deserializers know to include the custom // data we have on the children as well. - [DataMember(Name = "children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public new RoslynDocumentSymbol[]? Children { get => (RoslynDocumentSymbol[]?)base.Children; diff --git a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs index ac79269e2d44b..15f26944b6da3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs @@ -2,8 +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.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -18,7 +18,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal class RoslynDocumentSymbolParams : DocumentSymbolParams { - [JsonProperty(PropertyName = "useHierarchicalSymbols", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("useHierarchicalSymbols")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool UseHierarchicalSymbols { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs index 735db7ae8fa8c..98efe6212d2a3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs @@ -2,12 +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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record DebugAttachParams( - [property: DataMember(Name = "processId")] int ProcessId + [property: JsonPropertyName("processId")] int ProcessId ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs index f273254c0bdc7..cee2e897c0f14 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs @@ -2,12 +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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record DebugAttachResult( - [property: DataMember(Name = "didAttach")] bool DidAttach + [property: JsonPropertyName("didAttach")] bool DidAttach ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs index f02f51dc1b58b..6329d24a20070 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs @@ -3,21 +3,19 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record RunTestsParams( - [property: DataMember(Name = "textDocument")] LSP.TextDocumentIdentifier TextDocument, - [property: DataMember(Name = "range")] LSP.Range Range, - [property: DataMember(Name = "attachDebugger")] bool AttachDebugger, - [property: DataMember(Name = "runSettingsPath")] string? RunSettingsPath + [property: JsonPropertyName("textDocument")] LSP.TextDocumentIdentifier TextDocument, + [property: JsonPropertyName("range")] LSP.Range Range, + [property: JsonPropertyName("attachDebugger")] bool AttachDebugger, + [property: JsonPropertyName("runSettingsPath")] string? RunSettingsPath ) : LSP.IPartialResultParams { - [DataMember(Name = LSP.Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(LSP.Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs index 08d3455c26db5..91e81544887f1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs @@ -2,14 +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.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record RunTestsPartialResult( - [property: DataMember(Name = "stage")] string Stage, - [property: DataMember(Name = "message")] string Message, - [property: DataMember(Name = "progress"), JsonProperty(NullValueHandling = NullValueHandling.Ignore)] TestProgress? Progress + [property: JsonPropertyName("stage")] string Stage, + [property: JsonPropertyName("message")] string Message, + [property: JsonPropertyName("progress"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] TestProgress? Progress ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs index 4c5911cadfa7b..8ecff54eec170 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs @@ -2,14 +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.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record struct TestProgress( - [property: DataMember(Name = "testsPassed")] long TestsPassed, - [property: DataMember(Name = "testsFailed")] long TestsFailed, - [property: DataMember(Name = "testsSkipped")] long TestsSkipped, - [property: DataMember(Name = "totalTests")] long TotalTests + [property: JsonPropertyName("testsPassed")] long TestsPassed, + [property: JsonPropertyName("testsFailed")] long TestsFailed, + [property: JsonPropertyName("testsSkipped")] long TestsSkipped, + [property: JsonPropertyName("totalTests")] long TotalTests ); diff --git a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs index c2145c42bf68c..24dd76f87ba7e 100644 --- a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.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.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer @@ -14,7 +14,7 @@ internal interface ILanguageServerFactory { public AbstractLanguageServer Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, diff --git a/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs b/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs index 641e7f1953f1d..e833c82c7047f 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; -using System.Linq; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs index 28e824ca250fd..4319937d232ab 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Composition; -using System.Text; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs index 7e4930168f914..2ede34011caaf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from a server to a client for the workspace/applyEdit request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ApplyWorkspaceEditParams { /// /// Gets or sets the label associated with this edit. /// - [DataMember(Name = "label")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("label")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Label { get; @@ -29,7 +27,7 @@ public string? Label /// /// Gets or sets the edit to be applied to the workspace. /// - [DataMember(Name = "edit")] + [JsonPropertyName("edit")] public WorkspaceEdit Edit { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs index 080ce8850873f..0fb22929337ba 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response sent for a workspace/applyEdit request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] + internal class ApplyWorkspaceEditResponse { /// /// Gets or sets a value indicating whether edits were applied or not. /// - [DataMember(Name = "applied")] + [JsonPropertyName("applied")] public bool Applied { get; @@ -28,8 +27,8 @@ public bool Applied /// /// Gets or sets a string with textual description for why the edit was not applied. /// - [DataMember(Name = "failureReason")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("failureReason")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FailureReason { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs index aa135aefed881..ba9367aea66dd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents client capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ClientCapabilities { /// /// Gets or sets the workspace capabilities. /// - [DataMember(Name = "workspace")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspace")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceClientCapabilities? Workspace { get; @@ -29,8 +27,8 @@ public WorkspaceClientCapabilities? Workspace /// /// Gets or sets the text document capabilities. /// - [DataMember(Name = "textDocument")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textDocument")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextDocumentClientCapabilities? TextDocument { get; @@ -40,8 +38,8 @@ public TextDocumentClientCapabilities? TextDocument /// /// Gets or sets the experimental capabilities. /// - [DataMember(Name = "experimental")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("experimental")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Experimental { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs index 86df1605432f0..9ac8ab11410e9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing a change that can be performed in code. A CodeAction must either set @@ -14,13 +13,12 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeAction { /// /// Gets or sets the human readable title for this code action. /// - [DataMember(Name = "title")] + [JsonPropertyName("title")] public string Title { get; @@ -30,8 +28,8 @@ public string Title /// /// Gets or sets the kind of code action this instance represents. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind? Kind { get; @@ -41,8 +39,8 @@ public CodeActionKind? Kind /// /// Gets or sets the diagnostics that this code action resolves. /// - [DataMember(Name = "diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Diagnostic[]? Diagnostics { get; @@ -52,8 +50,8 @@ public Diagnostic[]? Diagnostics /// /// Gets or sets the workspace edit that this code action performs. /// - [DataMember(Name = "edit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("edit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEdit? Edit { get; @@ -63,8 +61,8 @@ public WorkspaceEdit? Edit /// /// Gets or sets the command that this code action executes. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -74,8 +72,8 @@ public Command? Command /// /// Gets or sets the data that will be resend to the server if the code action is selected to be resolved. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs index 2ccf570a49f2d..e40c24c7a3bd6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing diagnostic information about the context of a code action /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionContext { /// /// Gets or sets an array of diagnostics relevant to a code action. /// - [DataMember(Name = "diagnostics")] + [JsonPropertyName("diagnostics")] public Diagnostic[] Diagnostics { get; @@ -28,8 +26,8 @@ public Diagnostic[] Diagnostics /// /// Gets or sets an array of code action kinds to filter for. /// - [DataMember(Name = "only")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("only")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind[]? Only { get; @@ -39,8 +37,8 @@ public CodeActionKind[]? Only /// /// Gets or sets the indicating how the code action was triggered.. /// - [DataMember(Name = "triggerKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionTriggerKind? TriggerKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs index 264756a446c2c..c6c8663e284b2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of a code action. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct CodeActionKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs index 1ec354679a037..03806a7c4c696 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class containing the set of code action kinds that are supported. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionKindSetting { /// /// Gets or sets the code actions kinds the client supports. /// - [DataMember(Name = "valueSet")] + [JsonPropertyName("valueSet")] public CodeActionKind[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs index 92e1cc4a4f9b7..adc2cf397db90 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing support for code action literals. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionLiteralSetting { /// /// Gets or sets a value indicating what code action kinds are supported. /// - [DataMember(Name = "codeActionKind")] + [JsonPropertyName("codeActionKind")] public CodeActionKindSetting CodeActionKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs index 3b32c4f3ec6bf..e63a4852b80e8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the registration options for code actions support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionOptions : IWorkDoneProgressOptions { /// @@ -22,8 +20,8 @@ internal class CodeActionOptions : IWorkDoneProgressOptions /// The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server /// may list out every specific kind they provide. /// - [DataMember(Name = "codeActionKinds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionKinds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind[]? CodeActionKinds { get; @@ -33,16 +31,16 @@ public CodeActionKind[]? CodeActionKinds /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets a value indicating whether the server provides support to resolve /// additional information for a code action. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs index 67f546f31ba48..70e9f70d93c4a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for the textDocument/codeAction request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionParams : ITextDocumentParams { /// /// Gets or sets the document identifier indicating where the command was invoked. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range in the document for which the command was invoked. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -37,7 +36,7 @@ public Range Range /// /// Gets or sets the additional diagnostic information about the code action context. /// - [DataMember(Name = "context")] + [JsonPropertyName("context")] public CodeActionContext Context { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs index 6649a9adf92c9..1d1280be4d074 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for codeAction/resolve support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties")] + [JsonPropertyName("properties")] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs index 4af0f9f18dd66..eba4e7a023559 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for code action support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating the client supports code action literals. /// - [DataMember(Name = "codeActionLiteralSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionLiteralSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionLiteralSetting? CodeActionLiteralSupport { get; @@ -31,8 +29,8 @@ public CodeActionLiteralSetting? CodeActionLiteralSupport /// additional code action properties via a separate `codeAction/resolve` /// request. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionResolveSupportSetting? ResolveSupport { get; @@ -44,8 +42,8 @@ public CodeActionResolveSupportSetting? ResolveSupport /// property which is preserved between a `textDocument/codeAction` and a /// `codeAction/resolve` request. /// - [DataMember(Name = "dataSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("dataSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DataSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs index 14413d38f3ead..ed48b55932d33 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various reason why code actions were requested. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum CodeActionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs index b91705db37fb0..d7c414285753e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a description for an error code. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeDescription : IEquatable { /// /// Gets or sets URI to open with more information about the diagnostic error. /// - [DataMember(Name = "href")] + [JsonPropertyName("href")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Href { diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs index c88d783e26953..4bec557ca0a10 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing a code lens command that should be shown alongside source code. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLens { /// /// Gets or sets the range that the code lens applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -28,8 +26,8 @@ public Range Range /// /// Gets or sets the command associated with this code lens. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -39,8 +37,8 @@ public Command? Command /// /// Gets or sets the data that should be preserved between a textDocument/codeLens request and a codeLens/resolve request. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs index eddb78e41bf39..22ac4b4cbfb1c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for code lens support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether or not the code lens support has a resolve provider. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -29,8 +27,8 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs index d58fea7b19dec..a2ee3f149fbc2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for a textDocument/codeLens request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensParams : ITextDocumentParams { /// /// Gets or sets the document identifier to fetch code lens results for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs index 76543e6b1a334..444bf3390dabe 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.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. -using Newtonsoft.Json; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol { @@ -12,14 +11,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Color.cs b/src/Features/LanguageServer/Protocol/Protocol/Color.cs index bda5111332f02..319d60cf17462 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Color.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Color.cs @@ -4,14 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a color. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Color { /// @@ -20,7 +19,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "red")] + [JsonPropertyName("red")] public decimal Red { get; set; } /// @@ -29,7 +28,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "green")] + [JsonPropertyName("green")] public decimal Green { get; set; } /// @@ -38,7 +37,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "blue")] + [JsonPropertyName("blue")] public decimal Blue { get; set; } /// @@ -47,7 +46,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "alpha")] + [JsonPropertyName("alpha")] public decimal Alpha { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs index e2cf2f0433fad..a55e45f655455 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs @@ -4,26 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents color information. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ColorInformation { /// /// Gets or sets the text range representing the color. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; set; } /// /// Gets or sets the color. /// - [DataMember(Name = "color")] + [JsonPropertyName("color")] public Color Color { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Command.cs b/src/Features/LanguageServer/Protocol/Protocol/Command.cs index d3effcdb1b3ae..eefac1539a6f7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Command.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Command.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a reference to a command /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Command { /// /// Gets or sets the title of the command. /// - [DataMember(Name = "title")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("title")] + [JsonRequired] public string Title { get; @@ -29,8 +27,8 @@ public string Title /// /// Gets or sets the identifier associated with the command. /// - [DataMember(Name = "command")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("command")] + [JsonRequired] public string CommandIdentifier { get; @@ -40,8 +38,8 @@ public string CommandIdentifier /// /// Gets or sets the arguments that the command should be invoked with. /// - [DataMember(Name = "arguments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("arguments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object[]? Arguments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs index 609242748d6b3..6c674e6b35b65 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing additional information about the content in which a completion request is triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionContext { /// /// Gets or sets the indicating how the completion was triggered. /// - [DataMember(Name = "triggerKind")] + [JsonPropertyName("triggerKind")] public CompletionTriggerKind TriggerKind { get; @@ -28,8 +26,8 @@ public CompletionTriggerKind TriggerKind /// /// Gets or sets the character that triggered code completion. /// - [DataMember(Name = "triggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TriggerCharacter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs index f416ae767d3d1..d795c27940feb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs @@ -6,21 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents an IntelliSense completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItem { /// /// Gets or sets the label value, i.e. display text to users. /// - [DataMember(Name = "label", IsRequired = true)] + [JsonPropertyName("label")] + [JsonRequired] public string Label { get; @@ -30,8 +29,8 @@ public string Label /// /// Gets or sets additional details for the label. /// - [DataMember(Name = "labelDetails")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("labelDetails")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemLabelDetails? LabelDetails { get; @@ -41,11 +40,11 @@ public CompletionItemLabelDetails? LabelDetails /// /// Gets or sets the completion kind. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(CompletionItemKind.None)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemKind Kind { get; @@ -55,8 +54,8 @@ public CompletionItemKind Kind /// /// Tags for this completion item. /// - [DataMember(Name = "tags")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemTag[]? Tags { get; @@ -66,8 +65,8 @@ public CompletionItemTag[]? Tags /// /// Gets or sets the completion detail. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -77,8 +76,8 @@ public string? Detail /// /// Gets or sets the documentation comment. /// - [DataMember(Name = "documentation")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public SumType? Documentation { get; @@ -88,8 +87,8 @@ public SumType? Documentation /// /// Gets or sets a value indicating whether this should be the selected item when showing. /// - [DataMember(Name = "preselect")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("preselect")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Preselect { get; @@ -99,8 +98,8 @@ public bool Preselect /// /// Gets or sets the custom sort text. /// - [DataMember(Name = "sortText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("sortText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? SortText { get; @@ -110,8 +109,8 @@ public string? SortText /// /// Gets or sets the custom filter text. /// - [DataMember(Name = "filterText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("filterText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FilterText { get; @@ -121,8 +120,8 @@ public string? FilterText /// /// Gets or sets the insert text. /// - [DataMember(Name = "insertText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? InsertText { get; @@ -132,10 +131,10 @@ public string? InsertText /// /// Gets or sets the insert text format. /// - [DataMember(Name = "insertTextFormat")] + [JsonPropertyName("insertTextFormat")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [DefaultValue(InsertTextFormat.Plaintext)] public InsertTextFormat InsertTextFormat { @@ -146,8 +145,8 @@ public InsertTextFormat InsertTextFormat /// /// Gets or sets the text edit. /// - [DataMember(Name = "textEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? TextEdit { get; @@ -157,8 +156,8 @@ public SumType? TextEdit /// /// Gets or sets the text edit text. /// - [DataMember(Name = "textEditText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEditText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TextEditText { get; @@ -171,8 +170,8 @@ public string? TextEditText /// /// Additional text edits must not interfere with the main text edit. /// - [DataMember(Name = "additionalTextEdits")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("additionalTextEdits")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit[]? AdditionalTextEdits { get; @@ -184,8 +183,8 @@ public TextEdit[]? AdditionalTextEdits /// If present, this will override . /// If absent, will be used instead. /// - [DataMember(Name = "commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? CommitCharacters { get; @@ -198,8 +197,8 @@ public string[]? CommitCharacters /// /// This feature is not supported in VS. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -209,8 +208,8 @@ public Command? Command /// /// Gets or sets any additional data that links the unresolve completion item and the resolved completion item. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs index 2322270f54e5d..b80c3219ce3fa 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the initialization setting for completion item kind /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemKindSetting { /// /// Gets or sets the values that the client supports. /// - [DataMember(Name = "valueSet")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("valueSet")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemKind[]? ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs index 43b290c77361b..9bac8f809f335 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Additional details for a completion item label. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemLabelDetails { /// /// Gets or sets an optional string which is rendered less prominently directly after label, without any spacing. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -29,8 +27,8 @@ public string? Detail /// /// Gets or sets an optional string which is rendered less prominently after detail. /// - [DataMember(Name = "description")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Description { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs index 9686561c69c7b..11d0c8cd77ad1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents completion item capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemOptions { /// /// Gets or sets a value indicating The server has support for completion item label details /// - [DataMember(Name = "labelDetailsSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelDetailsSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelDetailsSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs index 58c655a5256c5..e5ca68bae6da4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemSetting { /// /// Gets or sets a value indicating whether completion items can contain snippets. /// - [DataMember(Name = "snippetSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("snippetSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SnippetSupport { get; @@ -29,8 +27,8 @@ public bool SnippetSupport /// /// Gets or sets a value indicating whether the client supports commit characters. /// - [DataMember(Name = "commitCharactersSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("commitCharactersSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CommitCharactersSupport { get; @@ -40,8 +38,8 @@ public bool CommitCharactersSupport /// /// Gets or sets the content formats supported for documentation. /// - [DataMember(Name = "documentationFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentationFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? DocumentationFormat { get; @@ -51,8 +49,8 @@ public MarkupKind[]? DocumentationFormat /// /// Gets or sets the a value indicating whether the client supports the deprecated property on a completion item. /// - [DataMember(Name = "deprecatedSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("deprecatedSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DeprecatedSupport { get; @@ -62,8 +60,8 @@ public bool DeprecatedSupport /// /// Gets or sets the a value indicating whether the client supports the preselect property on a completion item. /// - [DataMember(Name = "preselectSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("preselectSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PreselectSupport { get; @@ -73,8 +71,8 @@ public bool PreselectSupport /// /// Gets or sets the a value indicating whether the client supports the tag property on a completion item. /// - [DataMember(Name = "tagSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tagSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemTagSupportSetting? TagSupport { get; @@ -84,8 +82,8 @@ public CompletionItemTagSupportSetting? TagSupport /// /// Gets or sets the a value indicating whether the client supports insert replace edit. /// - [DataMember(Name = "insertReplaceSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("insertReplaceSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool InsertReplaceSupport { get; @@ -95,8 +93,8 @@ public bool InsertReplaceSupport /// /// Gets or sets the a value indicating which properties a client can resolve lazily on a completion item. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ResolveSupportSetting? ResolveSupport { get; @@ -106,8 +104,8 @@ public ResolveSupportSetting? ResolveSupport /// /// Gets or sets the a value indicating whether the client supports the `insertTextMode` property on a completion item to override the whitespace handling mode as defined by the client. /// - [DataMember(Name = "insertTextModeSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextModeSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextModeSupportSetting? InsertTextModeSupport { get; @@ -117,8 +115,8 @@ public InsertTextModeSupportSetting? InsertTextModeSupport /// /// Gets or sets the a value indicating whether the client supports completion item label details. /// - [DataMember(Name = "labelDetailsSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelDetailsSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelDetailsSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs index 94b64986cff53..0b79975a7393a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for the tag property on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemTagSupportSetting { /// /// Gets or sets a value indicating the tags supported by the client. /// - [DataMember(Name = "valueSet", IsRequired = true)] + [JsonPropertyName("valueSet")] + [JsonRequired] public CompletionItemTag[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs index 6189603e856f0..122fcb04d4e64 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs @@ -4,23 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a completion list. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionList { /// /// Gets or sets a value indicating whether Items is the complete list of items or not. If incomplete is true, then /// filtering should ask the server again for completion item. /// - [DataMember(Name = "isIncomplete")] + [JsonPropertyName("isIncomplete")] public bool IsIncomplete { get; @@ -30,7 +27,7 @@ public bool IsIncomplete /// /// Gets or sets the list of completion items. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public CompletionItem[] Items { get; @@ -40,8 +37,8 @@ public CompletionItem[] Items /// /// Gets or sets the completion list item defaults. /// - [DataMember(Name = "itemDefaults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("itemDefaults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionListItemDefaults? ItemDefaults { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs index 9c30582f3790a..4487a31a0f843 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents default properties associated with the entire completion list. /// - [DataContract] internal class CompletionListItemDefaults { /// /// Gets or sets the default commit character set. /// - [DataMember(Name = "commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? CommitCharacters { get; @@ -27,8 +25,8 @@ public string[]? CommitCharacters /// /// Gets or sets the default edit range. /// - [DataMember(Name = "editRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("editRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? EditRange { get; @@ -38,8 +36,8 @@ public SumType? EditRange /// /// Gets or sets the default . /// - [DataMember(Name = "insertTextFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextFormat? InsertTextFormat { get; @@ -49,8 +47,8 @@ public InsertTextFormat? InsertTextFormat /// /// Gets or sets the default . /// - [DataMember(Name = "insertTextMode")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextMode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextMode? InsertTextMode { get; @@ -60,8 +58,8 @@ public InsertTextMode? InsertTextMode /// /// Gets or sets the default completion item data. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs index 6ae332b4c314b..ee4a5d56d6e8e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents capabilites for the completion list type. /// - [DataContract] internal class CompletionListSetting { /// /// Gets or sets a value containing the supported property names of the object. /// If omitted, no properties are supported. /// - [DataMember(Name = "itemDefaults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("itemDefaults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? ItemDefaults { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs index 95c31bb7af59b..bdfa14e9507da 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents completion capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionOptions : IWorkDoneProgressOptions { /// /// Gets or sets the trigger characters. /// - [DataMember(Name = "triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; @@ -29,8 +27,8 @@ public string[]? TriggerCharacters /// /// Gets or sets a value indicating all the possible commit characters associated with the language server. /// - [DataMember(Name = "allCommitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("allCommitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? AllCommitCharacters { get; @@ -40,8 +38,8 @@ public string[]? AllCommitCharacters /// /// Gets or sets a value indicating whether server provides completion item resolve capabilities. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -51,15 +49,15 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets completion item setting. /// - [DataMember(Name = "completionItem")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItem")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemOptions? CompletionItemOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs index 0756a75604049..0c1626b6c3395 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the textDocument/completion request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionParams : TextDocumentPositionParams, IPartialResultParams?> { /// /// Gets or sets the completion context. /// - [DataMember(Name = "context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionContext? Context { get; @@ -30,8 +28,8 @@ public CompletionContext? Context /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress?>? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs index d05a2bcac679e..30a5228668a92 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionSetting : DynamicRegistrationSetting { /// /// Gets or sets completion item setting. /// - [DataMember(Name = "completionItem")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItem")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemSetting? CompletionItem { get; @@ -29,8 +27,8 @@ public CompletionItemSetting? CompletionItem /// /// Gets or sets specific settings. /// - [DataMember(Name = "completionItemKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItemKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemKindSetting? CompletionItemKind { get; @@ -40,8 +38,8 @@ public CompletionItemKindSetting? CompletionItemKind /// /// Gets or sets a value indicating whether the client supports sending additional context. /// - [DataMember(Name = "contextSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("contextSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ContextSupport { get; @@ -51,8 +49,8 @@ public bool ContextSupport /// /// Gets or sets a value indicating client's default when the completion item doesn't provide an `insertTextMode` property. /// - [DataMember(Name = "insertTextMode")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextMode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextMode? InsertTextMode { get; @@ -62,8 +60,8 @@ public InsertTextMode? InsertTextMode /// /// Gets or sets a value indicating whether the client supports capabilities on the completion list. /// - [DataMember(Name = "completionList")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionList")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionListSetting? CompletionListSetting { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs index c6e0289f2196f..4154beeb6fa7f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways in which completion can be triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum CompletionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs index 28bf61c08039e..51c7596a161c8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents an configuration item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ConfigurationItem { /// /// Gets or sets the scope to get the configuration section for. /// - [DataMember(Name = "scopeUri")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("scopeUri")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(DocumentUriConverter))] public Uri? ScopeUri { @@ -31,8 +29,8 @@ public Uri? ScopeUri /// /// Gets or sets the requested configuration section. /// - [DataMember(Name = "section")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("section")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Section { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs index c4547e2f08587..3cfc4fd4a19cf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the workspace/configuration request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ConfigurationParams { /// /// Gets or sets the ConfigurationItems being requested. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public ConfigurationItem[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs index 1da728f7de61d..4a91a4cacbde4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs @@ -2,59 +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. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Globalization; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// TODO: document. - /// - internal class DocumentUriConverter : JsonConverter - { - /// - public override bool CanConvert(Type objectType) - { - return true; - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - if (reader.TokenType == JsonToken.String) - { - var token = JToken.ReadFrom(reader); - var uri = new Uri(token.ToObject()); +using System; +using System.Text.Json; +using System.Text.Json.Serialization; - return uri; - } - else if (reader.TokenType == JsonToken.Null) - { - return null; - } +namespace Roslyn.LanguageServer.Protocol; - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.DocumentUriSerializationError, reader.Value)); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); +/// +/// TODO: document. +/// +internal class DocumentUriConverter : JsonConverter +{ + public override Uri Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => new(reader.GetString()); - if (value is Uri uri) - { - var token = JToken.FromObject(uri.AbsoluteUri); - token.WriteTo(writer); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {nameof(Uri)}"); - } - } - } + public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options) + => writer.WriteStringValue(value.AbsoluteUri); } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs deleted file mode 100644 index 207ed168f25e6..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs +++ /dev/null @@ -1,19 +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. - -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; - - /// - /// Class containing extension method to thread-safely manage operations. - /// - internal static class JsonConverterCollectionUtilities - { - /// - /// Lock used for modifications to Converters collection. - /// - public static readonly object ConvertersLock = new object(); - } -} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs new file mode 100644 index 0000000000000..c69b5f8bfbeb8 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.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.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +// copied from https://github.com/dotnet/runtime/issues/98038 to match newtonsoft behavior +internal class NaturalObjectConverter : JsonConverter +{ + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => ReadObjectCore(ref reader); + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + var runtimeType = value.GetType(); + if (runtimeType == typeof(object)) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + } + else + { + JsonSerializer.Serialize(writer, value, runtimeType, options); + } + } + + private static object? ReadObjectCore(ref Utf8JsonReader reader) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + return null; + + case JsonTokenType.False or JsonTokenType.True: + return reader.GetBoolean(); + + case JsonTokenType.Number: + if (reader.TryGetInt32(out var intValue)) + { + return intValue; + } + if (reader.TryGetInt64(out var longValue)) + { + return longValue; + } + + return reader.GetDouble(); + + case JsonTokenType.String: + return reader.GetString(); + + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var element = ReadObjectCore(ref reader); + list.Add(element); + } + return list; + + case JsonTokenType.StartObject: + return JsonSerializer.Deserialize(ref reader); + + default: + throw new JsonException(); + } + } +} \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs index ca7ba65725eb1..ff7e382b22a64 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs @@ -2,64 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; - /// - /// JsonConverter to correctly deserialize int arrays in the Label param of ParameterInformation. - /// - internal class ParameterInformationConverter : JsonConverter +/// +/// JsonConverter to correctly deserialize int arrays in the Label param of ParameterInformation. +/// +internal class ParameterInformationConverter : JsonConverter +{ + public override ParameterInformation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - public override bool CanWrite => false; + using var document = JsonDocument.ParseValue(ref reader); + var root = document.RootElement; - /// - public override bool CanConvert(Type objectType) - { - return true; - } + var parameter = new ParameterInformation(); - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + if (root.TryGetProperty("label", out var labelElement)) { - var token = JToken.Load(reader); - - var label = ((JObject)token).Property("label", StringComparison.Ordinal); - var documentation = ((JObject)token).Property("documentation", StringComparison.Ordinal); - - var parameter = new ParameterInformation(); - - if (label != null) + if (labelElement.ValueKind == JsonValueKind.Array) { - var value = label.Value; - if (value is JArray arr) - { - var tuple = new Tuple(arr[0].Value(), arr[1].Value()); - parameter.Label = tuple; - } - else - { - // If label is not an array we can serialize it normally - parameter.Label = value.ToObject>>(); - } + parameter.Label = new Tuple(labelElement[0].GetInt32(), labelElement[1].GetInt32()); } - - if (documentation != null) + else { - var value = documentation.Value; - parameter.Documentation = value.ToObject?>(); + parameter.Label = labelElement.Deserialize>>(options); } + } - return parameter; + if (root.TryGetProperty("documentation", out var documentationElement)) + { + parameter.Documentation = documentationElement.Deserialize>(options); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + return parameter; + } + + public override void Write(Utf8JsonWriter writer, ParameterInformation value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("label"); + JsonSerializer.Serialize(writer, value.Label, options); + + if (value.Documentation != null) { - throw new NotImplementedException(); + writer.WritePropertyName("documentation"); + JsonSerializer.Serialize(writer, value.Documentation, options); } + + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs index 64d2ae3d53d6e..0fab79be3e263 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs @@ -2,22 +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. -namespace Roslyn.LanguageServer.Protocol; - using System; using System.ComponentModel; using System.Globalization; using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer; -using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; +namespace Roslyn.LanguageServer.Protocol; /// /// JsonConverter for serializing and deserializing string-based enums. /// /// The actual type implementing . internal class StringEnumConverter - : JsonConverter + : JsonConverter where TStringEnumType : IStringEnum { private static readonly Func CreateEnum; @@ -36,46 +35,30 @@ static StringEnumConverter() CreateEnum = Expression.Lambda>(body, param).Compile(); } - /// - public override bool CanConvert(Type objectType) => objectType == typeof(TStringEnumType); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override TStringEnumType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - - if (reader.TokenType == JsonToken.String) + if (reader.TokenType == JsonTokenType.String) { - return CreateEnum((string)reader.Value!); + return CreateEnum(reader.GetString()!); } - else if (reader.TokenType == JsonToken.Null) + else if (reader.TokenType == JsonTokenType.Null) { - return default(TStringEnumType); + return default; } - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.StringEnumSerializationError, reader.Value)); + throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.StringEnumSerializationError, reader.GetString())); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, TStringEnumType value, JsonSerializerOptions options) { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - if (value is TStringEnumType kind) - { - writer.WriteValue(kind.Value); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {typeof(TStringEnumType).FullName}"); - } + writer.WriteStringValue(value.Value); } /// /// Type converter from to . /// This is required to support . /// - internal class TypeConverter + public class TypeConverter : System.ComponentModel.TypeConverter { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs index 303bc1801deca..d8fb1d10670a9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs @@ -2,269 +2,296 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Roslyn.LanguageServer.Protocol; +internal class SumConverter : JsonConverterFactory { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// Converter to translate to and from SumTypes. - /// - internal class SumConverter : JsonConverter + public override bool CanConvert(Type typeToConvert) { - private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); + return typeof(ISumType).IsAssignableFrom(typeToConvert); + } - /// - public override bool CanConvert(Type objectType) - { - return typeof(ISumType).IsAssignableFrom(objectType); - } + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var converterType = typeof(SumConverter<>).MakeGenericType(typeToConvert); + return (JsonConverter)Activator.CreateInstance(converterType)!; + } - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + internal class SumTypeInfoCache + { + // netstandard1.0 doesn't support Array.Empty +#pragma warning disable CA1825 // Avoid zero-length array allocations. + private static readonly IReadOnlyList EmptyUnionInfos = new UnionTypeInfo[0]; +#pragma warning restore CA1825 // Avoid zero-length array allocations. - // Even if CanConvert only returns true for ISumType, ReadJson is invoked for Nullable> as well - if (reader.TokenType == JsonToken.Null) - { - if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - return null; - } - else - { - // We shouldn't really ever have a non-Nullable SumType being received as null but, if we do, return an "empty" SumType - return Activator.CreateInstance(objectType); - } - } + private readonly IReadOnlyList allUnionTypeInfos; - // objectType will be one of the various SumType variants. In order for this converter to work with all SumTypes it has to use reflection. - // This method works by attempting to deserialize the json into each of the type parameters to a SumType and stops at the first success. - var sumTypeInfoCache = SumTypeCache.GetOrAdd(objectType, (t) => new SumTypeInfoCache(t)); + private readonly IReadOnlyList primitiveUnionTypeInfos; - JToken? token = null; - var applicableUnionTypeInfos = sumTypeInfoCache.GetApplicableInfos(reader.TokenType); + private readonly IReadOnlyList arrayUnionTypeInfos; - for (var i = 0; i < applicableUnionTypeInfos.Count; i++) - { - var unionTypeInfo = applicableUnionTypeInfos[i]; + private readonly IReadOnlyList objectUnionTypeInfos; - if (!IsTokenCompatibleWithType(reader, unionTypeInfo)) - { - continue; - } + public SumTypeInfoCache(Type sumTypeType) + { + var allUnionTypeInfosSet = new List(); + List? primitiveUnionTypeInfosSet = null; + List? arrayUnionTypeInfosSet = null; + List? objectUnionTypeInfosSet = null; - try - { - object? sumValue; - if (token == null && i + 1 == applicableUnionTypeInfos.Count) - { - // We're at the very last entry, we don't need to maintain the JsonReader, can read directly from the JsonReader to avoid the inbetween JObject type. - sumValue = serializer.Deserialize(reader, unionTypeInfo.Type); - } - else - { - if (token == null) - { - token = JToken.ReadFrom(reader); - } + // If the SumType is a nullable extract the underlying type and re-assign + sumTypeType = NormalizeToNonNullable(sumTypeType); - if (unionTypeInfo.KindAttribute is not null && - (token is not JObject jObject || jObject[unionTypeInfo.KindAttribute.KindPropertyName]?.ToString() != unionTypeInfo.KindAttribute.Kind)) - { - continue; - } + var typeInfo = sumTypeType.GetTypeInfo(); + var parameterTypes = typeInfo.GenericTypeArguments; + foreach (var parameterType in parameterTypes) + { + var parameterTypeInfo = NormalizeToNonNullable(parameterType).GetTypeInfo(); + var declaredConstructor = typeInfo.GetConstructor(new Type[] { parameterType }) ?? + throw new ArgumentException(nameof(sumTypeType), "All constructor parameter types must be represented in the generic type arguments of the SumType"); - sumValue = token.ToObject(unionTypeInfo.Type, serializer); - } + var kindAttribute = parameterType.GetCustomAttribute(); + var unionTypeInfo = new UnionTypeInfo(parameterType, declaredConstructor, kindAttribute); + allUnionTypeInfosSet.Add(unionTypeInfo); - object?[] args = [sumValue]; - var sum = unionTypeInfo.Constructor.Invoke(args); - return sum; + if (parameterTypeInfo.IsPrimitive || + parameterTypeInfo == typeof(string) || + typeof(IStringEnum).IsAssignableFrom(parameterTypeInfo)) + { + primitiveUnionTypeInfosSet ??= new List(); + primitiveUnionTypeInfosSet.Add(unionTypeInfo); } - catch + else if (parameterTypeInfo.IsArray) { - continue; + arrayUnionTypeInfosSet ??= new List(); + arrayUnionTypeInfosSet.Add(unionTypeInfo); + } + else + { + objectUnionTypeInfosSet ??= new List(); + objectUnionTypeInfosSet.Add(unionTypeInfo); } } - throw new JsonSerializationException(LanguageServerProtocolResources.NoSumTypeMatch); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - if (value is null) + this.allUnionTypeInfos = allUnionTypeInfosSet; + this.primitiveUnionTypeInfos = primitiveUnionTypeInfosSet ?? EmptyUnionInfos; + this.arrayUnionTypeInfos = arrayUnionTypeInfosSet ?? EmptyUnionInfos; + if ((objectUnionTypeInfosSet?.Count ?? 0) > 1) { - writer.WriteNull(); + // If some types are tagged with a KindAttribute, make sure they are first in the list in order to avoid the wrong type being deserialized + this.objectUnionTypeInfos = objectUnionTypeInfosSet.Where(t => t.KindAttribute is not null).Concat( + objectUnionTypeInfosSet.Where(t => t.KindAttribute is null)).ToList(); } else { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - var sumValue = ((ISumType)value!).Value; - - if (sumValue != null) - { - var token = JToken.FromObject(sumValue); - token.WriteTo(writer); - } + this.objectUnionTypeInfos = objectUnionTypeInfosSet ?? EmptyUnionInfos; } } - private static bool IsTokenCompatibleWithType(JsonReader reader, SumTypeInfoCache.UnionTypeInfo unionTypeInfo) + public IReadOnlyList GetApplicableInfos(JsonTokenType startingTokenType) { - var isCompatible = true; - switch (reader.TokenType) + return startingTokenType switch { - case JsonToken.Float: - isCompatible = unionTypeInfo.Type == typeof(double) || - unionTypeInfo.Type == typeof(float); - break; - case JsonToken.Boolean: - isCompatible = unionTypeInfo.Type == typeof(bool); - break; - case JsonToken.Integer: - isCompatible = unionTypeInfo.Type == typeof(int) || - unionTypeInfo.Type == typeof(uint) || - unionTypeInfo.Type == typeof(long) || - unionTypeInfo.Type == typeof(ulong) || - unionTypeInfo.Type == typeof(short) || - unionTypeInfo.Type == typeof(ushort) || - unionTypeInfo.Type == typeof(byte) || - unionTypeInfo.Type == typeof(sbyte) || - unionTypeInfo.Type == typeof(double) || - unionTypeInfo.Type == typeof(float); - break; - case JsonToken.String: - isCompatible = unionTypeInfo.Type == typeof(string) || - typeof(IStringEnum).IsAssignableFrom(unionTypeInfo.Type); - break; - } - - return isCompatible; + JsonTokenType.StartArray + => this.arrayUnionTypeInfos, + JsonTokenType.Number or + JsonTokenType.String or + JsonTokenType.True or + JsonTokenType.False + => this.primitiveUnionTypeInfos, + JsonTokenType.StartObject + => this.objectUnionTypeInfos, + _ => this.allUnionTypeInfos, + }; } - private class SumTypeInfoCache + private static Type NormalizeToNonNullable(Type sumTypeType) { - // netstandard1.0 doesn't support Array.Empty -#pragma warning disable CA1825 // Avoid zero-length array allocations. - private static readonly IReadOnlyList EmptyUnionInfos = new UnionTypeInfo[0]; -#pragma warning restore CA1825 // Avoid zero-length array allocations. + return Nullable.GetUnderlyingType(sumTypeType) ?? sumTypeType; + } - private readonly IReadOnlyList allUnionTypeInfos; + public class UnionTypeInfo + { + // System.Text.Json can pre-compile the generic SumType<> constructor call so we don't need to do it through reflection every time. + internal delegate T StjReader(ref Utf8JsonReader reader, JsonSerializerOptions options); + + private static readonly Type[] expressionLambdaMethodTypes = new[] { typeof(Type), typeof(Expression), typeof(ParameterExpression[]) }; + private static readonly MethodInfo expressionLambdaMethod = typeof(Expression) + .GetMethods() + .Where(mi => + !mi.IsGenericMethod && + mi.Name == "Lambda" && + mi.GetParameters() + .Select(p => p.ParameterType) + .SequenceEqual(expressionLambdaMethodTypes)) + .Single(); + + private static readonly Type[] jsonSerializerDeserializeMethodTypes = new[] { typeof(Utf8JsonReader).MakeByRefType(), typeof(JsonSerializerOptions) }; + private static readonly MethodInfo jsonSerializerDeserializeMethod = typeof(JsonSerializer) + .GetMethods() + .Where(mi => + mi.IsGenericMethod && + mi.Name == "Deserialize" && + mi.GetParameters() + .Select(p => p.ParameterType) + .SequenceEqual(jsonSerializerDeserializeMethodTypes)) + .Single(); + + public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kindAttribute) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor)); + this.KindAttribute = kindAttribute; + + var param1 = Expression.Parameter(typeof(Utf8JsonReader).MakeByRefType(), "reader"); + var param2 = Expression.Parameter(typeof(JsonSerializerOptions), "options"); + var body = Expression.New( + constructor, + Expression.Call( + jsonSerializerDeserializeMethod.MakeGenericMethod(type), + param1, + param2)); + var expression = (LambdaExpression)expressionLambdaMethod.Invoke(null, new object[] { typeof(StjReader<>).MakeGenericType(constructor.DeclaringType), body, new[] { param1, param2 } })!; + + StjReaderFunction = expression.Compile(); + } - private readonly IReadOnlyList primitiveUnionTypeInfos; + public Type Type { get; } - private readonly IReadOnlyList arrayUnionTypeInfos; + public ConstructorInfo Constructor { get; } - private readonly IReadOnlyList objectUnionTypeInfos; + public KindAttribute? KindAttribute { get; } - public SumTypeInfoCache(Type sumTypeType) - { - var allUnionTypeInfosSet = new List(); - List? primitiveUnionTypeInfosSet = null; - List? arrayUnionTypeInfosSet = null; - List? objectUnionTypeInfosSet = null; + public object StjReaderFunction { get; } + } + } +} - // If the SumType is a nullable extract the underlying type and re-assign - sumTypeType = NormalizeToNonNullable(sumTypeType); +internal class SumConverter : JsonConverter + where T : struct, ISumType +{ + private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); - var typeInfo = sumTypeType.GetTypeInfo(); - var parameterTypes = typeInfo.GenericTypeArguments; - foreach (var parameterType in parameterTypes) - { - var parameterTypeInfo = NormalizeToNonNullable(parameterType).GetTypeInfo(); - var declaredConstructor = typeInfo.GetConstructor([parameterType]) ?? - throw new ArgumentException(nameof(sumTypeType), "All constructor parameter types must be represented in the generic type arguments of the SumType"); + /// + public override T Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + // We shouldn't really ever have a non-Nullable SumType being received as null but, if we do, return an "empty" SumType + return (T)Activator.CreateInstance(objectType)!; + } - var kindAttribute = parameterType.GetCustomAttribute(); - var unionTypeInfo = new UnionTypeInfo(parameterType, declaredConstructor, kindAttribute); - allUnionTypeInfosSet.Add(unionTypeInfo); + // objectType will be one of the various SumType variants. In order for this converter to work with all SumTypes it has to use reflection. + // This method works by attempting to deserialize the json into each of the type parameters to a SumType and stops at the first success. + var sumTypeInfoCache = SumTypeCache.GetOrAdd(objectType, (t) => new SumConverter.SumTypeInfoCache(t)); - if (parameterTypeInfo.IsPrimitive || - parameterTypeInfo == typeof(string) || - typeof(IStringEnum).IsAssignableFrom(parameterTypeInfo)) - { - primitiveUnionTypeInfosSet ??= []; - primitiveUnionTypeInfosSet.Add(unionTypeInfo); - } - else if (parameterTypeInfo.IsArray) - { - arrayUnionTypeInfosSet ??= []; - arrayUnionTypeInfosSet.Add(unionTypeInfo); - } - else + var applicableUnionTypeInfos = sumTypeInfoCache.GetApplicableInfos(reader.TokenType); + if (applicableUnionTypeInfos.Count > 0) + { + var backupReader = reader; + if (applicableUnionTypeInfos[0].KindAttribute is { } kindAttribute) + { + using var document = JsonDocument.ParseValue(ref reader); + reader = backupReader; + if (document.RootElement.TryGetProperty(kindAttribute.KindPropertyName, out var value)) + { + var kind = value.GetString(); + for (var i = 0; i < applicableUnionTypeInfos.Count; i++) { - objectUnionTypeInfosSet ??= []; - objectUnionTypeInfosSet.Add(unionTypeInfo); + var unionTypeInfo = applicableUnionTypeInfos[i]; + if (unionTypeInfo.KindAttribute == null) + { + throw new JsonException(LanguageServerProtocolResources.NoSumTypeMatch); + } + + if (unionTypeInfo.KindAttribute.Kind == kind) + { + var result = ((SumConverter.SumTypeInfoCache.UnionTypeInfo.StjReader)unionTypeInfo.StjReaderFunction).Invoke(ref reader, options); + return result; + } } } + } + + for (var i = 0; i < applicableUnionTypeInfos.Count; i++) + { + var unionTypeInfo = applicableUnionTypeInfos[i]; - this.allUnionTypeInfos = allUnionTypeInfosSet; - this.primitiveUnionTypeInfos = primitiveUnionTypeInfosSet ?? EmptyUnionInfos; - this.arrayUnionTypeInfos = arrayUnionTypeInfosSet ?? EmptyUnionInfos; - if ((objectUnionTypeInfosSet?.Count ?? 0) > 1) + if (!IsTokenCompatibleWithType(ref reader, unionTypeInfo)) { - // If some types are tagged with a KindAttribute, make sure they are first in the list in order to avoid the wrong type being deserialized - this.objectUnionTypeInfos = objectUnionTypeInfosSet.Where(t => t.KindAttribute is not null).Concat( - objectUnionTypeInfosSet.Where(t => t.KindAttribute is null)).ToList(); + continue; } - else + + if (unionTypeInfo.KindAttribute != null) { - this.objectUnionTypeInfos = objectUnionTypeInfosSet ?? EmptyUnionInfos; + continue; } - } - public IReadOnlyList GetApplicableInfos(JsonToken startingTokenType) - { - return startingTokenType switch + try { - JsonToken.StartArray - => this.arrayUnionTypeInfos, - JsonToken.Integer or - JsonToken.Float or - JsonToken.Bytes or - JsonToken.String or - JsonToken.Boolean - => this.primitiveUnionTypeInfos, - JsonToken.StartObject - => this.objectUnionTypeInfos, - _ => this.allUnionTypeInfos, - }; + var result = ((SumConverter.SumTypeInfoCache.UnionTypeInfo.StjReader)unionTypeInfo.StjReaderFunction).Invoke(ref reader, options); + return result; + } + catch + { + reader = backupReader; + continue; + } } + } - private static Type NormalizeToNonNullable(Type sumTypeType) - { - return Nullable.GetUnderlyingType(sumTypeType) ?? sumTypeType; - } + throw new JsonException(LanguageServerProtocolResources.NoSumTypeMatch); + } - internal class UnionTypeInfo - { - public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kindAttribute) - { - this.Type = type ?? throw new ArgumentNullException(nameof(type)); - this.Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor)); - this.KindAttribute = kindAttribute; - } + /// + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer = writer ?? throw new ArgumentNullException(nameof(writer)); - public Type Type { get; } + var sumValue = value.Value; - public ConstructorInfo Constructor { get; } + if (sumValue != null) + { + JsonSerializer.Serialize(writer, sumValue, options); + } + } - public KindAttribute? KindAttribute { get; } - } + private static bool IsTokenCompatibleWithType(ref Utf8JsonReader reader, SumConverter.SumTypeInfoCache.UnionTypeInfo unionTypeInfo) + { + var isCompatible = true; + switch (reader.TokenType) + { + case JsonTokenType.True: + case JsonTokenType.False: + isCompatible = unionTypeInfo.Type == typeof(bool); + break; + case JsonTokenType.Number: + isCompatible = unionTypeInfo.Type == typeof(int) || + unionTypeInfo.Type == typeof(uint) || + unionTypeInfo.Type == typeof(long) || + unionTypeInfo.Type == typeof(ulong) || + unionTypeInfo.Type == typeof(short) || + unionTypeInfo.Type == typeof(ushort) || + unionTypeInfo.Type == typeof(byte) || + unionTypeInfo.Type == typeof(sbyte) || + unionTypeInfo.Type == typeof(double) || + unionTypeInfo.Type == typeof(float); + break; + case JsonTokenType.String: + isCompatible = unionTypeInfo.Type == typeof(string) || + typeof(IStringEnum).IsAssignableFrom(unionTypeInfo.Type); + break; } + + return isCompatible; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs index 68bb6efadad73..64e236febb743 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs @@ -2,99 +2,48 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Roslyn.LanguageServer.Protocol; +internal class TextDocumentSyncConverter : JsonConverter { - using System; - using System.Globalization; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// Converter which offers custom serialization for enum to a object. - /// - /// - /// This is to support backwards compatibility for the protocol. - /// - internal class TextDocumentSyncConverter : JsonConverter + public override TextDocumentSyncOptions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - public override bool CanConvert(Type objectType) + if (reader.TokenType == JsonTokenType.Number) { - return true; - } - - /// - /// Deserializes a json value to a object. - /// - /// Reader from which to read json value. - /// Type of the json value. - /// Existing value. - /// Default serializer. - /// A which matches the json value. - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - if (reader.TokenType is JsonToken.Float or JsonToken.Integer) - { - // This conversion is modeled after what VS Code does, see https://github.com/Microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L1234 - var textDocSync = new TextDocumentSyncOptions - { - OpenClose = true, - Change = (TextDocumentSyncKind)int.Parse(reader.Value!.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture), - Save = new SaveOptions - { - IncludeText = false, - }, - }; - - return textDocSync; - } - else if (reader.TokenType == JsonToken.String) - { - return JsonConvert.DeserializeObject(reader.Value!.ToString()); - } - else if (reader.TokenType == JsonToken.StartObject) + // This conversion is modeled after what VS Code does, see https://github.com/microsoft/vscode-languageserver-node/blob/21f4f0af6bf1623483c3e65f36e550bfdb6245a6/client/src/common/client.ts#L1248 + var textDocSync = new TextDocumentSyncOptions { - var token = JToken.ReadFrom(reader); - return token.ToObject(); - } - else if (reader.TokenType == JsonToken.Null) - { - // This conversion is modeled after what VS Code does, see https://github.com/Microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L1234 - var textDocSync = new TextDocumentSyncOptions + OpenClose = true, + Change = (TextDocumentSyncKind)reader.GetInt32(), + Save = new SaveOptions { - OpenClose = true, - Change = TextDocumentSyncKind.None, - Save = new SaveOptions - { - IncludeText = false, - }, - }; - - return textDocSync; - } + IncludeText = false, + }, + }; - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.TextDocumentSyncSerializationError, reader.Value)); + return textDocSync; } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + else if (reader.TokenType == JsonTokenType.String) + { + var value = reader.GetString()!; + return JsonSerializer.Deserialize(value)!; + } + else if (reader.TokenType == JsonTokenType.StartObject) { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); + return JsonSerializer.Deserialize(ref reader, options)!; + } - if (value is null) - { - writer.WriteNull(); - } - else - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); + throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.TextDocumentSyncSerializationError, reader.GetString())); + } - var token = JToken.FromObject(value); - token.WriteTo(writer); - } - } + /// + public override void Write(Utf8JsonWriter writer, TextDocumentSyncOptions value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs b/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs index 8cc16ae3379ff..723850708e18b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("create")] internal class CreateFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "create"; /// /// Gets or sets the resource to create. /// - [DataMember(Name = "uri", IsRequired = true)] + [JsonPropertyName("uri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -38,8 +37,8 @@ public Uri Uri /// /// Gets or sets the additional options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CreateFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs index 68fcf7c8e30c5..c8215d3a14223 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CreateFileOptions { /// /// Gets or sets a value indicating whether the creation should overwrite the file if it already exists. (Overwrite wins over ignoreIfExists). /// - [DataMember(Name = "overwrite")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overwrite")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Overwrite { get; @@ -29,8 +27,8 @@ public bool Overwrite /// /// Gets or sets a value indicating whether the action should be ignored if the file already exists. /// - [DataMember(Name = "ignoreIfExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs b/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs index abcd66aa84d61..d221861d3f169 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DefaultBehaviorPrepareRename { /// /// Gets or sets a value indicating whether the rename position is valid and the client should use its /// default behavior to compute the rename range. /// - [DataMember(Name = "defaultBehavior")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("defaultBehavior")] + [JsonRequired] public bool DefaultBehavior { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs index 2469f7a97db8e..b906658d84875 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DefinitionOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs b/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs index dcc6eed78e854..919093f482981 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a delete file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("delete")] internal class DeleteFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "delete"; /// /// Gets or sets the file to delete. /// - [DataMember(Name = "uri", IsRequired = true)] + [JsonPropertyName("uri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -38,8 +37,8 @@ public Uri Uri /// /// Gets or sets the additional options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DeleteFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs index 5e1c8edd9e884..8764e43d01657 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs @@ -4,23 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DeleteFileOptions { /// /// Gets or sets a value indicating whether the delete operation should be applied recursively if a folder is denoted. (Overwrite wins over ignoreIfNotExists). /// - [DataMember(Name = "recursive")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("recursive")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Recursive { get; @@ -30,8 +27,8 @@ public bool Recursive /// /// Gets or sets a value indicating whether the action should be ignored if the file doesn't exists. /// - [DataMember(Name = "ignoreIfNotExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfNotExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfNotExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs b/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs index d664f263d5a7b..57def31e7cee4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Linq; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a source code diagnostic message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Diagnostic : IEquatable { /// /// Gets or sets the source code range. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -30,8 +28,8 @@ public Range Range /// /// Gets or sets the diagnostic severity. /// - [DataMember(Name = "severity")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("severity")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticSeverity? Severity { get; @@ -44,8 +42,8 @@ public DiagnosticSeverity? Severity /// /// The value can be an , . /// - [DataMember(Name = "code")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("code")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Code { get; @@ -55,8 +53,8 @@ public SumType? Code /// /// Gets or sets an optional value that describes the error code. /// - [DataMember(Name = "codeDescription")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeDescription")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeDescription? CodeDescription { get; @@ -67,8 +65,8 @@ public CodeDescription? CodeDescription /// Gets or sets a human-readable string describing the source of this /// diagnostic, e.g. 'typescript' or 'super lint'. It usually appears in the user interface. /// - [DataMember(Name = "source")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("source")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Source { get; @@ -78,7 +76,7 @@ public string? Source /// /// Gets or sets the diagnostic's message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; @@ -88,8 +86,8 @@ public string Message /// /// Gets or sets the diagnostic's tags. /// - [DataMember(Name = "tags")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticTag[]? Tags { get; @@ -99,8 +97,8 @@ public DiagnosticTag[]? Tags /// /// Gets or sets the diagnostic related information /// - [DataMember(Name = "relatedInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticRelatedInformation[]? RelatedInformation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs index e464c6eb9a8a8..4d51c1b96d201 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs @@ -4,29 +4,27 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Server capabilities for pull diagnostics. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets the identifier in which the diagnostics are bucketed by the client. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -36,7 +34,7 @@ public string? Identifier /// /// Gets or sets a value indicating whether the language has inter file dependencies. /// - [DataMember(Name = "interFileDependencies")] + [JsonPropertyName("interFileDependencies")] public bool InterFileDependencies { get; @@ -46,7 +44,7 @@ public bool InterFileDependencies /// /// Gets or sets a value indicating whether the server provides support for workspace diagnostics as well. /// - [DataMember(Name = "workspaceDiagnostics")] + [JsonPropertyName("workspaceDiagnostics")] public bool WorkspaceDiagnostics { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs index 549fe02ed56cf..710b99742f84e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; /// /// Diagnostic registration options. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticRegistrationOptions : DiagnosticOptions, IStaticRegistrationOptions, ITextDocumentRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; @@ -29,8 +27,8 @@ public DocumentFilter[]? DocumentSelector /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "id")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs index e62e8058f8cf7..cc3099afe6976 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs @@ -4,7 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a related message and source code location for a diagnostic. @@ -13,19 +13,18 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DiagnosticRelatedInformation { /// /// Gets or sets the location for the related information. /// - [DataMember(Name = "location")] + [JsonPropertyName("location")] public Location Location { get; set; } /// /// Gets or sets the message for the related information. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs index 9840373f3f4c5..0ee406d772ae8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing the cancellation data returned from a diagnostic request. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticServerCancellationData { /// /// Gets or sets a value indicating whether the client should re-trigger the request. /// - [DataMember(Name = "retriggerRequest")] + [JsonPropertyName("retriggerRequest")] public bool RetriggerRequest { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs index 75c0827e25bf6..b5d623d11f2c9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Client settings for pull diagnostics. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports related documents for document diagnostic pulls. /// - [DataMember(Name = "relatedDocumentSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("relatedDocumentSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RelatedDocumentSupport { get; set; } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs index 1c1640598d134..5aee040d2afc3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the workspace diagnostic client capabilities. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs index a9816d3926583..102c94551b080 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with workspace/didChangeConfiguration requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeConfigurationParams { /// /// Gets or sets the settings that are applicable to the language server. /// - [DataMember(Name = "settings")] + [JsonPropertyName("settings")] public object Settings { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs index 0a4f9c3934a02..78ac42a0491d8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didChange message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the document that changed. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public VersionedTextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public VersionedTextDocumentIdentifier TextDocument /// /// Gets or sets the content changes. /// - [DataMember(Name = "contentChanges")] + [JsonPropertyName("contentChanges")] public TextDocumentContentChangeEvent[] ContentChanges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs index 75e21d2e8de99..e090da167da4e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with workspace/didChangeWatchedFiles message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeWatchedFilesParams { /// /// Gets or sets of the collection of file change events. /// - [DataMember(Name = "changes")] + [JsonPropertyName("changes")] public FileEvent[] Changes { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs index f2e1dccc784a4..15a1ccb176d6e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didClose message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidCloseTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the text document identifier. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs index 2d32742a3c94f..ff6ecfacc3ccb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didOpen message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidOpenTextDocumentParams { /// /// Gets or sets the which represents the text document that was opened. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentItem TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs index aab858ce354e8..b0c24f6e168c2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with a textDocument/didSave message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidSaveTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the which represents the text document that was saved. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -28,8 +26,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the which represents the content of the text document when it was saved. /// - [DataMember(Name = "text")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("text")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs index dbad3759bf15b..96cf410caca2e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentColorOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs index 7f364f077b7cb..6e5b7506a718b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentColor request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentColorParams : ITextDocumentParams { /// /// Gets or sets the to provide links for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs index 55cd0c4f38eb7..348274675e055 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the document diagnostic request parameters /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DocumentDiagnosticParams : ITextDocumentParams, IPartialResultParams> { /// @@ -23,8 +21,8 @@ internal class DocumentDiagnosticParams : ITextDocumentParams, IPartialResultPar /// Note that the first literal send needs to be either the or /// followed by n literals. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress>? PartialResultToken { get; @@ -34,7 +32,7 @@ public IProgress /// Gets or sets the to provide diagnostics for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -44,8 +42,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the identifier for which the client is requesting diagnostics for. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -55,8 +53,8 @@ public string? Identifier /// /// Gets or sets the result id of a previous diagnostics response if provided. /// - [DataMember(Name = "previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs index 936ad48f1dbde..9e2c17cb63aef 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs @@ -6,20 +6,19 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing a partial document diagnostic report for a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DocumentDiagnosticReportPartialResult { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] + [JsonPropertyName("relatedDocuments")] public Dictionary> RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs index d06ad65ed2e9a..da8ed8d6512a1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a filter over certain types of documents /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFilter { /// /// Gets or sets a language id for the filter (e.g. 'typescript'). /// - [DataMember(Name = "language")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("language")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Language { get; @@ -29,8 +27,8 @@ public string? Language /// /// Gets or sets a Uri scheme (e.g. 'file' or 'untitled'). /// - [DataMember(Name = "scheme")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("scheme")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Scheme { get; @@ -40,8 +38,8 @@ public string? Scheme /// /// Gets or sets a glob pattern (e.g. '*.cs'). /// - [DataMember(Name = "pattern")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("pattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Pattern { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs index 24f1e6f2008e9..144e53f17087c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document formatting options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFormattingOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs index 766dc19e1843e..35f814d3ff9b9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/formatting message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFormattingParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be formatted. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the formatting options. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs index 692bc31ab9b6f..ee7ab022fea0f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response from a textDocument/documentHighlight request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentHighlight { /// /// Gets or sets the range that the highlight applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -30,11 +28,11 @@ public Range Range /// /// Gets or sets the kind of highlight. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(DocumentHighlightKind.Text)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public DocumentHighlightKind Kind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs index 60375fb94cb92..7ec465f1fc335 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the different types of document highlight. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum DocumentHighlightKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs index 40333c450d29b..c01c7eb95adb7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentHighlightOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs index 65107dad7c797..9fed8bdcc35fe 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentHighlight request. @@ -20,8 +19,8 @@ internal class DocumentHighlightParams /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = "partialResultToken")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("partialResultToken")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs index 91e3d65073174..a4fe991aef507 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of a textDocument/documentLink request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLink { /// /// Gets or sets the range the link applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -29,9 +27,9 @@ public Range Range /// /// Gets or sets the uri that the link points to. /// - [DataMember(Name = "target")] + [JsonPropertyName("target")] [JsonConverter(typeof(DocumentUriConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Uri? Target { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs index 42625d49b51a9..c01515f8ae949 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document link options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLinkOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether or not the server supports resolve providers. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -29,8 +27,8 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs index 58aaf921ffbc4..780a15fceded5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentLink request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLinkParams : ITextDocumentParams { /// /// Gets or sets the to provide links for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs index c358c3dc1b7be..13105e53003d7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for on type formatting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentOnTypeFormattingOptions { /// /// Gets or sets the first trigger character. /// - [DataMember(Name = "firstTriggerCharacter")] + [JsonPropertyName("firstTriggerCharacter")] public string FirstTriggerCharacter { get; @@ -28,8 +26,8 @@ public string FirstTriggerCharacter /// /// Gets or sets additional trigger characters. /// - [DataMember(Name = "moreTriggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("moreTriggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? MoreTriggerCharacter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs index eda0db4a326e2..919c36c1275ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/onTypeFormatting request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentOnTypeFormattingParams : TextDocumentPositionParams { /// /// Gets or sets the character that was typed. /// - [DataMember(Name = "ch")] + [JsonPropertyName("ch")] public string Character { get; @@ -27,7 +26,7 @@ public string Character /// /// Gets or sets the for the request. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs index 8dc450883122a..1c4cbe2ce2c7c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document range formatting options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentRangeFormattingOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs index 103881f93ef17..104830b737a1b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/rangeFormatting message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentRangeFormattingParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be formatted. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the selection range to be formatted. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -37,7 +36,7 @@ public Range Range /// /// Gets or sets the formatting options. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs index bb1f719958c7b..e9110e2f82bc0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be @@ -14,13 +13,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbol { /// /// Gets or sets the name of this symbol. /// - [DataMember(IsRequired = true, Name = "name")] + [JsonPropertyName("name")] + [JsonRequired] public string Name { get; @@ -30,8 +29,8 @@ public string Name /// /// Gets or sets more detail for this symbol, e.g the signature of a function. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -41,7 +40,7 @@ public string? Detail /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public SymbolKind Kind { get; @@ -51,8 +50,8 @@ public SymbolKind Kind /// /// Gets or sets a value indicating whether this symbol is deprecated. /// - [DataMember(Name = "deprecated")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("deprecated")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Deprecated { get; @@ -64,7 +63,8 @@ public bool Deprecated /// like comments.This information is typically used to determine if the clients cursor is /// inside the symbol to reveal in the symbol in the UI. /// - [DataMember(IsRequired = true, Name = "range")] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -75,7 +75,8 @@ public Range Range /// Gets or sets the range that should be selected and revealed when this symbol is being picked, e.g the name of a function. /// Must be contained by the `range`. /// - [DataMember(IsRequired = true, Name = "selectionRange")] + [JsonPropertyName("selectionRange")] + [JsonRequired] public Range SelectionRange { get; @@ -85,8 +86,8 @@ public Range SelectionRange /// /// Gets or sets the children of this symbol, e.g. properties of a class. /// - [DataMember(Name = "children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentSymbol[]? Children { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs index 2ea84c8ca9ca6..9298ac3d94632 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs index 3ba1eaa41230b..b12f359aec536 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with textDocument/documentSymbol requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolParams : ITextDocumentParams { /// /// Gets or sets the text document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs index f1df03c9304ef..6b4274a7d9fdc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the initialization setting for document symbols. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolSetting : DynamicRegistrationSetting { /// /// Gets or sets the capabilities. /// - [DataMember(Name = "symbolKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbolKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKindSetting? SymbolKind { get; @@ -29,8 +27,8 @@ public SymbolKindSetting? SymbolKind /// /// Gets or sets a value indicating whether the document has hierarchical symbol support. /// - [DataMember(Name = "hierarchicalDocumentSymbolSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("hierarchicalDocumentSymbolSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool HierarchicalDocumentSymbolSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs index 095b552f61176..9bc210064c78f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a setting that can be dynamically registered. /// - [DataContract] internal class DynamicRegistrationSetting { /// @@ -32,8 +30,8 @@ public DynamicRegistrationSetting(bool value) /// /// Gets or sets a value indicating whether setting can be dynamically registered. /// - [DataMember(Name = "dynamicRegistration")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("dynamicRegistration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DynamicRegistration { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs index 6cdae0d11cafc..d79e56516f134 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for execute command support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ExecuteCommandOptions : IWorkDoneProgressOptions { /// /// Gets or sets the commands that are to be executed on the server. /// - [DataMember(Name = "commands")] + [JsonPropertyName("commands")] public string[] Commands { get; @@ -28,8 +26,8 @@ public string[] Commands /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs index d9c934063e7ba..6269066a2ad07 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from client to server for the workspace/executeCommand request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ExecuteCommandParams { /// /// Gets or sets the command identifier associated with the command handler. /// - [DataMember(Name = "command")] + [JsonPropertyName("command")] public string Command { get; @@ -28,8 +26,8 @@ public string Command /// /// Gets or sets the arguments that the command should be invoked with. /// - [DataMember(Name = "arguments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("arguments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object[]? Arguments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs index 70f3ed31e8acb..dd53e04c4e9f6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs @@ -5,7 +5,8 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Converter used to serialize and deserialize classes extending types defined in the @@ -14,28 +15,46 @@ namespace Roslyn.LanguageServer.Protocol /// Base class that is specified in the /// Microsoft.VisualStudio.LanguageServer.Protocol package. /// Extension class that extends TBase. - internal class VSExtensionConverter : JsonConverter - where TExtension : TBase + internal class VSExtensionConverter : JsonConverter + where TExtension : TBase { - /// - public override bool CanWrite => false; + private JsonSerializerOptions? _trimmedOptions; - /// - public override bool CanConvert(Type objectType) + public override TBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return objectType == typeof(TBase); + return JsonSerializer.Deserialize(ref reader, options); } - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options) { - return serializer.Deserialize(reader); + // System.Text.Json doesn't serialize properties from derived classes by default, and there's no 'readonly' converters + // like Newtonsoft has. + if (value is TExtension extension) + { + JsonSerializer.Serialize(writer, extension, options); + } + else + { + // There's no ability to fallback to a 'default' serialization, so we clone our options + // and exclude this converter from it to prevent a stack overflow. + JsonSerializer.Serialize(writer, (object)value!, DropConverter(options)); + } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + private JsonSerializerOptions DropConverter(JsonSerializerOptions options) { - throw new NotImplementedException(); + if (_trimmedOptions != null) + { + return _trimmedOptions; + } + + lock (this) + { + options = new System.Text.Json.JsonSerializerOptions(options); + options.Converters.Remove(this); + _trimmedOptions = options; + return options; + } } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs index 51ce11b3bf2bc..0cd191528ca56 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs @@ -2,56 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Utility functions to simplify working with the Visual Studio extensions to the Language Server Protocol. +/// +internal static class VSExtensionUtilities +{ /// - /// Utility functions to simplify working with the Visual Studio extensions to the Language Server Protocol. + /// Adds to the allowing to + /// deserialize the JSON stream into objects which include Visual Studio specific extensions. + /// + /// For example, it allows to correctly deserialize the entries of a + /// 'codeAction/resolve' request into objects even if + /// is defined as an array of . /// - internal static class VSExtensionUtilities + internal static void AddVSExtensionConverters(this JsonSerializerOptions options) { - /// - /// Adds to the allowing to - /// deserialize the JSON stream into objects which include Visual Studio specific extensions. - /// - /// For example, it allows to correctly deserialize the entries of a - /// 'codeAction/resolve' request into objects even if - /// is defined as an array of . - /// - /// - /// If is used in parallel to the execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that the - /// collection is not modified when is in use. - /// - /// Instance of to be configured. - public static void AddVSExtensionConverters(this JsonSerializer serializer) - { - // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + AddConverters(options.Converters); + } - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); + private static void AddConverters(IList converters) + { + // Reading the number of converters before we start adding new ones + var existingConvertersCount = converters.Count; + + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); - void TryAddConverter() - where TExtension : TBase + void TryAddConverter() + where TExtension : TBase + { + for (var i = 0; i < existingConvertersCount; i++) { - for (var i = 0; i < existingConvertersCount; i++) + var existingConverterType = converters[i].GetType(); + if (existingConverterType.IsGenericType && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && + existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - var existingConverterType = serializer.Converters[i].GetType(); - if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && - existingConverterType.GenericTypeArguments[0] == typeof(TBase)) - { - return; - } + return; } - - serializer.Converters.Add(new VSExtensionConverter()); } + + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs index 4ac6e47c0a682..aa14da1ed5762 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs @@ -4,65 +4,63 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSDiagnostic : Diagnostic { /// /// Gets or sets the project and context (e.g. Win32, MacOS, etc.) in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projects")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projects")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSDiagnosticProjectInformation[]? Projects { get; set; } /// /// Gets or sets an expanded description of the diagnostic. /// - [DataMember(Name = "_vs_expandedMessage")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_expandedMessage")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ExpandedMessage { get; set; } /// /// Gets or sets a message shown when the user hovers over an error. If , then /// is used (use to prevent a tool tip from being shown). /// - [DataMember(Name = "_vs_toolTip")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_toolTip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ToolTip { get; set; } /// /// Gets or sets a non-human-readable identier allowing consolidation of multiple equivalent diagnostics /// (e.g. the same syntax error from builds targeting different platforms). /// - [DataMember(Name = "_vs_identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; set; } /// /// Gets or sets a string describing the diagnostic types (e.g. Security, Performance, Style, etc.). /// - [DataMember(Name = "_vs_diagnosticType")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticType")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DiagnosticType { get; set; } /// /// Gets or sets a rank associated with this diagnostic, used for the default sort. /// will be used if no rank is specified. /// - [DataMember(Name = "_vs_diagnosticRank")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticRank")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSDiagnosticRank? DiagnosticRank { get; set; } /// /// Gets or sets an ID used to associate this diagnostic with a corresponding line in the output window. /// - [DataMember(Name = "_vs_outputId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_outputId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? OutputId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs index c2a84f512d6eb..7cfc6d2346dc2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs @@ -4,35 +4,33 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// represents the project and context in which the is generated. /// - [DataContract] internal class VSDiagnosticProjectInformation { /// /// Gets or sets a human-readable identifier for the project in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// /// Gets or sets a human-readable identifier for the build context (e.g. Win32 or MacOS) /// in which the diagnostic was generated. /// - [DataMember(Name = "_vs_context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Context { get; set; } /// /// Gets or sets the unique identifier for the project in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projectIdentifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectIdentifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectIdentifier { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs index f02ebbc2bf112..e357fe57b13ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs @@ -4,19 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the parameter that is sent /// with the 'textDocument/_vs_getProjectContexts' request. /// - [DataContract] internal class VSGetProjectContextsParams { /// /// Gets or sets the document for which project contexts are queried. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentItem TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs index ed16c06838842..87a5115add07d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs @@ -5,7 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the unique identifier for a Visual Studio image asset. @@ -13,13 +13,12 @@ namespace Roslyn.LanguageServer.Protocol /// A list of valid image ids can be retrieved from the KnownMonikers class /// from the Visual Studio SDK. /// - [DataContract] internal class VSImageId : IEquatable { /// /// Gets or sets the component of the unique identifier. /// - [DataMember(Name = "_vs_guid")] + [JsonPropertyName("_vs_guid")] public Guid Guid { get; @@ -29,7 +28,7 @@ public Guid Guid /// /// Gets or sets the integer component of the unique identifier. /// - [DataMember(Name = "_vs_id")] + [JsonPropertyName("_vs_id")] public int Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs index a34ea77a25296..1a68fa04259ca 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSLocation : Location { /// /// Gets or sets the project name to be displayed to user. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// @@ -25,8 +23,8 @@ internal class VSLocation : Location /// In case the actual path on disk would be confusing for users, this should be a friendly display name. /// This doesn't have to correspond to a real file path, but must be parsable by the method. /// - [DataMember(Name = "_vs_displayPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_displayPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DisplayPath { get; set; } } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs index de37989753381..528a49257dea7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs @@ -5,18 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents a project context. /// - [DataContract] internal class VSProjectContext : IEquatable { /// /// Gets or sets the label for the project context. /// - [DataMember(Name = "_vs_label", IsRequired = true)] + [JsonPropertyName("_vs_label")] + [JsonRequired] public string Label { get; @@ -26,7 +26,8 @@ public string Label /// /// Gets or sets the unique identifier of the project context. /// - [DataMember(Name = "_vs_id", IsRequired = true)] + [JsonPropertyName("_vs_id")] + [JsonRequired] public string Id { get; @@ -36,7 +37,7 @@ public string Id /// /// Gets or sets the context kind of the project context which is used to determine its associated icon. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public VSProjectKind Kind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs index 4c1ee69a0bbff..2cca38acf08ac 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs @@ -4,19 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the response to the /// 'textDocument/_vs_getProjectContexts' request. /// - [DataContract] internal class VSProjectContextList { /// /// Gets or sets the document contexts associated with a text document. /// - [DataMember(Name = "_vs_projectContexts")] + [JsonPropertyName("_vs_projectContexts")] public VSProjectContext[] ProjectContexts { get; @@ -26,7 +25,7 @@ public VSProjectContext[] ProjectContexts /// /// Gets or sets the index of the default entry of the array. /// - [DataMember(Name = "_vs_defaultIndex")] + [JsonPropertyName("_vs_defaultIndex")] public int DefaultIndex { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs index 6a541c5bce361..467374d98fdce 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs @@ -4,12 +4,9 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// represents the various kinds of contexts. /// - [DataContract] internal enum VSProjectKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs index 3cd00971b4a28..0605c8afbba7f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends allowing to provide /// additional capabilities supported by Visual Studio. /// - [DataContract] internal class VSServerCapabilities : ServerCapabilities { /// /// Gets or sets a value indicating whether the server supports the /// 'textDocument/_vs_getProjectContexts' request. /// - [DataMember(Name = "_vs_projectContextProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_projectContextProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ProjectContextProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs index 62b8b120be35e..84d77d40b6219 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs @@ -4,34 +4,32 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSSymbolInformation : SymbolInformation { /// /// Gets or sets the icon associated with the symbol. If specified, this icon is used instead of . /// - [DataMember(Name = "_vs_icon")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_icon")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSImageId? Icon { get; set; } /// /// Gets or sets the description of the symbol. /// - [DataMember(Name = "_vs_description")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Description { get; set; } /// /// Gets or sets the hint text for the symbol. /// - [DataMember(Name = "_vs_hintText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_hintText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? HintText { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs index 2cffba1c8d1d8..519e178848056 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the project context of the text document. /// - [DataMember(Name = "_vs_projectContext")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectContext")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSProjectContext? ProjectContext { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs b/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs index 7d437e7bd1acb..6133abc68ec98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a file change event. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FileEvent { /// /// Gets or sets the URI of the file. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the file change type. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public FileChangeType FileChangeType { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs index a45d49f3f9d5f..78f0c975a205d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a folding range in a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRange { /// /// Gets or sets the start line value. /// - [DataMember(Name = "startLine")] + [JsonPropertyName("startLine")] public int StartLine { get; @@ -28,8 +26,8 @@ public int StartLine /// /// Gets or sets the start character value. /// - [DataMember(Name = "startCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("startCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? StartCharacter { get; @@ -39,7 +37,7 @@ public int? StartCharacter /// /// Gets or sets the end line value. /// - [DataMember(Name = "endLine")] + [JsonPropertyName("endLine")] public int EndLine { get; @@ -49,8 +47,8 @@ public int EndLine /// /// Gets or sets the end character value. /// - [DataMember(Name = "endCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("endCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? EndCharacter { get; @@ -60,8 +58,8 @@ public int? EndCharacter /// /// Gets or sets the folding range kind. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public FoldingRangeKind? Kind { get; @@ -71,8 +69,8 @@ public FoldingRangeKind? Kind /// /// Gets or sets the collapsedText. /// - [DataMember(Name = "collapsedText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("collapsedText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? CollapsedText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs index 05e12df4907f0..bf807c451becf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing various code action kinds. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct FoldingRangeKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs index c68fcaaa0a074..273d431e6da95 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the folding range provider options for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs index ac57b23d4f24c..fed7123c0cb67 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the folding range request parameter. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeParams : ITextDocumentParams { /// /// Gets or sets the text document associated with the folding range request. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs index f4c10c48e1f26..e57fde0b348aa 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the folding range setting for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeSetting : DynamicRegistrationSetting { /// /// Gets or sets the range limit for folding ranges. /// - [DataMember(Name = "rangeLimit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeLimit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? RangeLimit { get; @@ -29,8 +27,8 @@ public int? RangeLimit /// /// Gets or sets a value indicating whether if client only supports entire line folding only. /// - [DataMember(Name = "lineFoldingOnly")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("lineFoldingOnly")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LineFoldingOnly { get; @@ -40,8 +38,8 @@ public bool LineFoldingOnly /// /// Gets or sets a value indicating the specific options for the folding range. /// - [DataMember(Name = "foldingRange")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("foldingRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public FoldingRangeSettingOptions? FoldingRange { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs index f1a1fe75e4584..35b524fd67379 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the specific options for the folding range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeSettingOptions : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether if client supports collapsedText on folding ranges. /// - [DataMember(Name = "collapsedText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("collapsedText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CollapsedText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs index e1787620e30f5..c7385acedce78 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs @@ -5,22 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents formatting options. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FormattingOptions { /// /// Gets or sets the number of spaces to be inserted per tab. /// - [DataMember(Name = "tabSize")] + [JsonPropertyName("tabSize")] public int TabSize { get; @@ -30,7 +27,7 @@ public int TabSize /// /// Gets or sets a value indicating whether tabs should be spaces. /// - [DataMember(Name = "insertSpaces")] + [JsonPropertyName("insertSpaces")] public bool InsertSpaces { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs index ab55018017cb3..17eaadd82514a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a diagnostic report with a full set of problems. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class FullDocumentDiagnosticReport { /// /// Gets the kind of this report. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] #pragma warning disable CA1822 // Mark members as static public string Kind => DocumentDiagnosticReportKind.Full; #pragma warning restore CA1822 // Mark members as static @@ -27,8 +25,8 @@ internal class FullDocumentDiagnosticReport /// /// Gets or sets the optional result id. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; @@ -38,7 +36,7 @@ public string? ResultId /// /// Gets or sets the diagnostics in this report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public Diagnostic[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Hover.cs b/src/Features/LanguageServer/Protocol/Protocol/Hover.cs index 339ca873bcc6d..27c8399906956 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Hover.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Hover.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the data returned by a textDocument/hover request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Hover { /// @@ -21,7 +19,7 @@ internal class Hover /// If the object is not an array it can be of type , , or . /// // This is nullable because in VS we allow null when VSInternalHover.RawContent is specified instead of Contents - [DataMember(Name = "contents")] + [JsonPropertyName("contents")] public SumType[], MarkupContent>? Contents { get; @@ -31,8 +29,8 @@ public SumType[], MarkupCont /// /// Gets or sets the range over which the hover applies. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs index 0d5001c8105d4..51c8a83658f0d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the server hover support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class HoverOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs index f1d195fb0a6d8..e19b54f371fe0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the initialization setting for hover. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class HoverSetting : DynamicRegistrationSetting { /// /// Gets or sets the values supported. /// - [DataMember(Name = "contentFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("contentFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? ContentFormat { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs index 8a4cc0363a9bd..afd33c2190764 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ImplementationOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs index 756906f6d62c7..070add9aecfd7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the error type sent when the initialize request fails. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeError { /// /// Gets or sets a value indicating whether or not to retry. /// - [DataMember(Name = "retry")] + [JsonPropertyName("retry")] public bool Retry { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs index 3f412b17040a5..63a3ed253409c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the possible reasons for an initialization error. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum InitializeErrorCode { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs index a3f76104ae979..c0f68e4f7ad23 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with an initialize method request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeParams { /// /// Gets or sets the ID of the process which launched the language server. /// - [DataMember(Name = "processId")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("processId")] public int? ProcessId { get; @@ -35,8 +33,8 @@ public int? ProcessId /// Uses IETF language tags as the value's syntax. /// (See https://en.wikipedia.org/wiki/IETF_language_tag) /// - [DataMember(Name = "locale")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("locale")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Locale { get; @@ -46,8 +44,8 @@ public string? Locale /// /// Gets or sets the workspace root path. /// - [DataMember(Name = "rootPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rootPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [Obsolete("Deprecated in favour of RootUri")] public string? RootPath { @@ -61,8 +59,7 @@ public string? RootPath /// /// This should be a string representation of an URI. /// - [DataMember(Name = "rootUri")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("rootUri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri? RootUri { @@ -73,8 +70,8 @@ public Uri? RootUri /// /// Gets or sets the initialization options as specified by the client. /// - [DataMember(Name = "initializationOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("initializationOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? InitializationOptions { get; @@ -84,7 +81,7 @@ public object? InitializationOptions /// /// Gets or sets the capabilities supported by the client. /// - [DataMember(Name = "capabilities")] + [JsonPropertyName("capabilities")] public ClientCapabilities Capabilities { get; @@ -94,9 +91,9 @@ public ClientCapabilities Capabilities /// /// Gets or sets the initial trace setting. /// - [DataMember(Name = "trace")] + [JsonPropertyName("trace")] [DefaultValue(typeof(TraceSetting), "off")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public TraceSetting Trace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs index 8970f738ee2b2..b3cf86842bdfd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the result returned by the initialize request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeResult { /// /// Gets or sets the server capabilities. /// - [DataMember(Name = "capabilities")] + [JsonPropertyName("capabilities")] public ServerCapabilities Capabilities { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs index e710324261479..3b934f479d901 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing inlay hints that appear next to parameters or types. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHint { /// /// Gets or sets the position that the inlay hint applies to. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; @@ -28,7 +26,7 @@ public Position Position /// /// Gets or sets the label associated with this inlay hint. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public SumType Label { get; @@ -38,8 +36,8 @@ public SumType Label /// /// Gets or sets the InlayHintKind associated with this inlay hint. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintKind? Kind { get; @@ -49,8 +47,8 @@ public InlayHintKind? Kind /// /// Gets or sets the TextEdits associated with this inlay hint. /// - [DataMember(Name = "textEdits")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEdits")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit[]? TextEdits { get; @@ -60,8 +58,8 @@ public TextEdit[]? TextEdits /// /// Gets or sets the tooltip of this inlay hint. /// - [DataMember(Name = "tooltip")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tooltip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ToolTip { get; @@ -71,8 +69,8 @@ public SumType? ToolTip /// /// Gets or sets the padding before this inlay hint. /// - [DataMember(Name = "paddingLeft")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("paddingLeft")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PaddingLeft { get; @@ -82,8 +80,8 @@ public bool PaddingLeft /// /// Gets or sets the padding after this inlay hint. /// - [DataMember(Name = "paddingRight")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("paddingRight")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PaddingRight { get; @@ -93,8 +91,8 @@ public bool PaddingRight /// /// Gets or sets the data that should be preserved between a textDocument/inlayHint request and a inlayHint/resolve request. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs index 26b3d742d0007..9e8ccb1548903 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum values for inlay hint kinds. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum InlayHintKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs index f3ae678a2f27f..1946a23322ced 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// A class representing inlay hint label parts. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintLabelPart { /// /// Gets or sets the value associated with this label part. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; @@ -28,8 +26,8 @@ public string Value /// /// Gets or sets the tooltip of this label part. /// - [DataMember(Name = "tooltip")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tooltip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public SumType? ToolTip { get; @@ -39,8 +37,8 @@ public SumType? ToolTip /// /// Gets or sets the location of this label part. /// - [DataMember(Name = "location")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("location")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Location? Location { get; @@ -50,8 +48,8 @@ public Location? Location /// /// Gets or sets the command of this label part. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs index b86910b8786b0..ce9e1c5aef33a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs @@ -4,29 +4,27 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Server capabilities for inlay hints. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets a value indicating whether or not the inlay hints support has a resolve provider. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs index 84556a6ec5a3f..9761b7c09c83d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for a textDocument/inlayHint request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintParams : ITextDocumentParams { /// /// Gets or sets the document identifier to fetch inlay hints results for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range to fetch inlay hints results for. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs index 5825a4c023ac5..9df07a8275017 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Inlay hint registration options. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintRegistrationOptions : InlayHintOptions, ITextDocumentRegistrationOptions, IStaticRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; @@ -29,8 +27,8 @@ public DocumentFilter[]? DocumentSelector /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "id")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs index 560985840651c..25d1c6a154cc3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing settings for inlayHint/resolve support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties")] + [JsonPropertyName("properties")] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs index edce450da136b..d6f8008ba47b6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for inlay hint support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports /// resolving lazily on an inlay hint. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintResolveSupportSetting? ResolveSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs index 5060f0e640602..7308f9b3d2ec5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.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. -using Newtonsoft.Json; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol { @@ -12,14 +11,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs index 9f34cd35362cd..44c06ee7f3aef 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// A special text edit to provide an insert and a replace operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InsertReplaceEdit { /// /// Gets or sets the string to be inserted. /// - [DataMember(Name = "newText", IsRequired = true)] + [JsonPropertyName("newText")] + [JsonRequired] public string NewText { get; @@ -27,7 +27,8 @@ public string NewText /// /// Gets or sets the range range if the insert is requested /// - [DataMember(Name = "insert", IsRequired = true)] + [JsonPropertyName("insert")] + [JsonRequired] public Range Insert { get; @@ -37,7 +38,8 @@ public Range Insert /// /// Gets or sets the range range if the replace is requested /// - [DataMember(Name = "replace", IsRequired = true)] + [JsonPropertyName("replace")] + [JsonRequired] public Range Replace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs index 2ffbf04d520c6..26fc9e2c6efaf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents default range of InsertReplaceEdit for the entire completion list /// - [DataContract] internal class InsertReplaceRange { /// /// Gets or sets the insert range. /// - [DataMember(Name = "insert", IsRequired = true)] + [JsonPropertyName("insert")] + [JsonRequired] public Range Insert { get; @@ -25,7 +25,8 @@ public Range Insert /// /// Gets or sets the replace edit range. /// - [DataMember(Name = "replace", IsRequired = true)] + [JsonPropertyName("replace")] + [JsonRequired] public Range Replace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs index 3de5c8aa688cb..7b7e24c261a79 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for the tag property on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InsertTextModeSupportSetting { /// /// Gets or sets a value indicating the client supports the `insertTextMode` property on a completion item to override the whitespace handling mode as defined by the client. /// - [DataMember(Name = "valueSet", IsRequired = true)] + [JsonPropertyName("valueSet")] + [JsonRequired] public InsertTextMode[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs index c7a88f7485e0f..13545c7af9359 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs @@ -2,83 +2,71 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; -using System.Linq; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ClassifiedTextElementConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ClassifiedTextElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ClassifiedTextElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ClassifiedTextElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ClassifiedTextElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + List objects = new(); + + while (reader.Read()) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ClassifiedTextElement)) + if (reader.TokenType == JsonTokenType.EndObject) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextElement)}"); + return new ClassifiedTextElement(objects); } - var runTokens = data[nameof(ClassifiedTextElement.Runs)]?.ToArray() ?? - throw new JsonSerializationException($"Missing {nameof(ClassifiedTextElement.Runs)} property"); - var runs = new ClassifiedTextRun[runTokens.Length]; - for (var i = 0; i < runTokens.Length; i++) + if (reader.TokenType == JsonTokenType.PropertyName) { - var runTokenReader = runTokens[i].CreateReader(); - runTokenReader.Read(); - runs[i] = (ClassifiedTextRun)ClassifiedTextRunConverter.Instance.ReadJson(runTokenReader, typeof(ClassifiedTextRun), null, serializer)!; - } + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ClassifiedTextElement.Runs): + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; - return new ClassifiedTextElement(runs); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + objects.Add(ClassifiedTextRunConverter.Instance.Read(ref reader, typeof(ClassifiedTextRun), options)!); + } + + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ClassifiedTextElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextElement)}"); + break; + default: + reader.Skip(); + break; + } + } } + + throw new JsonException(); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ClassifiedTextElement value, JsonSerializerOptions options) { - if (value is not ClassifiedTextElement classifiedTextElement) + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ClassifiedTextElement.Runs)); + writer.WriteStartArray(); + foreach (var run in value.Runs) { - writer.WriteNull(); + ClassifiedTextRunConverter.Instance.Write(writer, run, options); } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ClassifiedTextElement.Runs)); - writer.WriteStartArray(); - foreach (var run in classifiedTextElement.Runs) - { - ClassifiedTextRunConverter.Instance.WriteJson(writer, run, serializer); - } - writer.WriteEndArray(); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ClassifiedTextElement)); - writer.WriteEndObject(); - } + writer.WriteEndArray(); + writer.WritePropertyName(ObjectContentConverter.TypeProperty); + writer.WriteStringValue(nameof(ClassifiedTextElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs index b4becb32c4bef..1b7fe24c3cce7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs @@ -2,91 +2,58 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ClassifiedTextRunConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ClassifiedTextRunConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ClassifiedTextRunConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) - => objectType == typeof(ClassifiedTextRun); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ClassifiedTextRun? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) + if (reader.TokenType == JsonTokenType.StartObject) { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) - { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ClassifiedTextRun)) + var data = JsonDocument.ParseValue(ref reader).RootElement; + if (data.TryGetProperty(ObjectContentConverter.TypeProperty, out var typeProperty) && typeProperty.GetString() != nameof(ClassifiedTextRun)) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextRun)}"); + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextRun)}"); } - var classificationTypeName = data[nameof(ClassifiedTextRun.ClassificationTypeName)]?.Value(); - var text = data[nameof(ClassifiedTextRun.Text)]?.Value(); - var markerTagType = data[nameof(ClassifiedTextRun.MarkerTagType)]?.Value(); - var style = (ClassifiedTextRunStyle)(data[nameof(ClassifiedTextRun.Style)]?.Value() ?? 0); - return new ClassifiedTextRun(classificationTypeName!, text!, style, markerTagType); + var classificationTypeName = data.GetProperty(nameof(ClassifiedTextRun.ClassificationTypeName)).GetString(); + var text = data.GetProperty(nameof(ClassifiedTextRun.Text)).GetString(); + var markerTagType = data.GetProperty(nameof(ClassifiedTextRun.MarkerTagType)).GetString(); + var style = (ClassifiedTextRunStyle)(data.GetProperty(nameof(ClassifiedTextRun.Style)).GetInt32()); + return new ClassifiedTextRun(classificationTypeName, text, style, markerTagType); } else { - throw new JsonSerializationException("Expected start object or null tokens"); + throw new JsonException("Expected start object or null tokens"); } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ClassifiedTextRun value, JsonSerializerOptions options) { - if (value is not ClassifiedTextRun classifiedTextRun) + writer.WriteStartObject(); + writer.WriteString(nameof(ClassifiedTextRun.ClassificationTypeName), value.ClassificationTypeName); + writer.WriteString(nameof(ClassifiedTextRun.Text), value.Text); + writer.WriteString(nameof(ClassifiedTextRun.MarkerTagType), value.MarkerTagType); + writer.WriteNumber(nameof(ClassifiedTextRun.Style), (int)value.Style); + writer.WriteNull(nameof(ClassifiedTextRun.Tooltip)); + if (value.Tooltip is not null) { - writer.WriteNull(); + throw new JsonException(); } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ClassifiedTextRun.ClassificationTypeName)); - writer.WriteValue(classifiedTextRun.ClassificationTypeName); - writer.WritePropertyName(nameof(ClassifiedTextRun.Text)); - writer.WriteValue(classifiedTextRun.Text); - writer.WritePropertyName(nameof(ClassifiedTextRun.MarkerTagType)); - writer.WriteValue(classifiedTextRun.MarkerTagType); - writer.WritePropertyName(nameof(ClassifiedTextRun.Style)); - writer.WriteValue(classifiedTextRun.Style); - writer.WritePropertyName(nameof(ClassifiedTextRun.Tooltip)); - writer.WriteNull(); - if (classifiedTextRun.Tooltip is not null) - { - throw new JsonSerializationException(); - } - writer.WritePropertyName(nameof(ClassifiedTextRun.NavigationAction)); - writer.WriteNull(); - if (classifiedTextRun.NavigationAction is not null) - { - throw new JsonSerializationException(); - } - - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ClassifiedTextRun)); - writer.WriteEndObject(); + writer.WriteNull(nameof(ClassifiedTextRun.NavigationAction)); + if (value.NavigationAction is not null) + { + throw new JsonException(); } + + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ClassifiedTextRun)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs index d1778d40c9251..39ab2a75d776f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs @@ -2,86 +2,81 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; -using System.Linq; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +namespace Roslyn.LanguageServer.Protocol; /// -/// JsonConverter for serializing and deserializing . +/// System.Text.Json.JsonConverter for serializing and deserializing . /// -internal class ContainerElementConverter : JsonConverter +internal class ContainerElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ContainerElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ContainerElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ContainerElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ContainerElement)) - { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ContainerElement)}"); - } + ContainerElementStyle? style = null; + List objects = new(); - var elementTokens = data[nameof(ContainerElement.Elements)]?.ToArray() ?? - throw new JsonSerializationException($"Missing {nameof(ContainerElement.Elements)} property"); - var elements = new object?[elementTokens.Length]; - for (var i = 0; i < elementTokens.Length; i++) + while (reader.Read()) { - var elementTokenReader = elementTokens[i].CreateReader(); - elementTokenReader.Read(); - elements[i] = ObjectContentConverter.Instance.ReadJson(elementTokenReader, typeof(object), null, serializer); - } + if (reader.TokenType == JsonTokenType.EndObject) + { + return new ContainerElement(style ?? throw new JsonException(), objects); + } - var style = (ContainerElementStyle)(data[nameof(ContainerElement.Style)]?.Value() ?? throw new JsonSerializationException()); - return new ContainerElement(style, elements); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ContainerElement.Elements): + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; + + objects.Add(ObjectContentConverter.Instance.Read(ref reader, typeof(object), options)!); + } + + break; + case nameof(ContainerElement.Style): + style = (ContainerElementStyle)reader.GetInt32(); + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ContainerElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ContainerElement)}"); + break; + default: + reader.Skip(); + break; + } + } + } } + + throw new JsonException(); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ContainerElement value, JsonSerializerOptions options) { - if (value is not ContainerElement containerElement) + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ContainerElement.Elements)); + writer.WriteStartArray(); + foreach (var run in value.Elements) { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ContainerElement.Elements)); - writer.WriteStartArray(); - foreach (var run in containerElement.Elements) - { - ObjectContentConverter.Instance.WriteJson(writer, run, serializer); - } - - writer.WriteEndArray(); - writer.WritePropertyName(nameof(ContainerElement.Style)); - writer.WriteValue(containerElement.Style); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ContainerElement)); - writer.WriteEndObject(); + ObjectContentConverter.Instance.Write(writer, run, options); } + writer.WriteEndArray(); + writer.WriteNumber(nameof(ContainerElement.Style), (int)value.Style); + writer.WritePropertyName(ObjectContentConverter.TypeProperty); + writer.WriteStringValue(nameof(ContainerElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs deleted file mode 100644 index 868de33bddf28..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs +++ /dev/null @@ -1,52 +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. - -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Linq; - using Newtonsoft.Json; - - /// - /// Converter used to deserialize objects dropping any property. - /// - internal class DropProgressConverter : JsonConverter - { - /// - public override bool CanWrite => true; - - /// - /// Static method to get a containing a . - /// - /// object containing a . - public static JsonSerializer CreateSerializer() - { - var serializer = new JsonSerializer(); - serializer.Converters.Add(new DropProgressConverter()); - return serializer; - } - - /// - public override bool CanConvert(Type objectType) - { - var isIProgressOfT = objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition().Equals(typeof(IProgress<>)); - var implementsIProgressOfT = objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(IProgress<>))); - - return isIProgressOfT || implementsIProgressOfT; - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - // We deserialize all IProgress objects as null. - return null; - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteNull(); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs index cb8d549fe6f65..6b0c54ede6d85 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs @@ -2,73 +2,66 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Core.Imaging; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ImageElementConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ImageElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ImageElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ImageElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ImageElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) + if (reader.TokenType == JsonTokenType.StartObject) { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) - { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ImageElement)) + ImageId? imageId = null; + string? automationName = null; + + while (reader.Read()) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageElement)}"); - } + if (reader.TokenType == JsonTokenType.EndObject) + { + imageId ??= imageId ?? throw new JsonException(); + return automationName is null ? new ImageElement(imageId.Value) : new ImageElement(imageId.Value, automationName); + } - var imageTokenReader = data[nameof(ImageElement.ImageId)]?.CreateReader() ?? throw new JsonSerializationException(); - imageTokenReader.Read(); - var imageId = (ImageId)ImageIdConverter.Instance.ReadJson(imageTokenReader, typeof(ImageId), null, serializer)!; - var automationName = data[nameof(ImageElement.AutomationName)]?.Value(); - return automationName is null ? new ImageElement(imageId) : new ImageElement(imageId, automationName); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ImageElement.ImageId): + imageId = ImageIdConverter.Instance.Read(ref reader, typeof(ImageId), options); + break; + case nameof(ImageElement.AutomationName): + automationName = reader.GetString(); + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ImageElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageElement)}"); + break; + default: + reader.Skip(); + break; + } + } + } } + + throw new JsonException("Expected start object or null tokens"); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ImageElement value, JsonSerializerOptions options) { - if (value is not ImageElement imageElement) - { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ImageElement.ImageId)); - ImageIdConverter.Instance.WriteJson(writer, imageElement.ImageId, serializer); - writer.WritePropertyName(nameof(ImageElement.AutomationName)); - writer.WriteValue(imageElement.AutomationName); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ImageElement)); - writer.WriteEndObject(); - } + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ImageElement.ImageId)); + ImageIdConverter.Instance.Write(writer, value.ImageId, options); + writer.WriteString(nameof(ImageElement.AutomationName), value.AutomationName); + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ImageElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs index 825d98d413ea5..80fba1a12ca49 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs @@ -2,71 +2,43 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Core.Imaging; -using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ImageIdConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ImageIdConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ImageIdConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ImageId); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ImageId Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ImageId)) + using var document = JsonDocument.ParseValue(ref reader); + var root = document.RootElement; + if (root.TryGetProperty(ObjectContentConverter.TypeProperty, out var typeProperty) && typeProperty.GetString() != nameof(ImageId)) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageId)}"); + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageId)}"); } - var guid = data[nameof(ImageId.Guid)]?.Value() ?? throw new JsonSerializationException(); - var id = data[nameof(ImageId.Id)]?.Value() ?? throw new JsonSerializationException(); + var guid = root.GetProperty(nameof(ImageId.Guid)).GetString() ?? throw new JsonException(); + var id = root.GetProperty(nameof(ImageId.Id)).GetInt32(); return new ImageId(new Guid(guid), id); } else { - throw new JsonSerializationException("Expected start object or null tokens"); + throw new JsonException("Expected start object or null tokens"); } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ImageId value, JsonSerializerOptions options) { - if (value is not ImageId imageId) - { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ImageId.Guid)); - writer.WriteValue(imageId.Guid); - writer.WritePropertyName(nameof(ImageId.Id)); - writer.WriteValue(imageId.Id); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ImageId)); - writer.WriteEndObject(); - } + writer.WriteStartObject(); + writer.WriteString(nameof(ImageId.Guid), value.Guid.ToString()); + writer.WriteNumber(nameof(ImageId.Id), value.Id); + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ImageId)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs index 26e0886a0ee51..428088de62230 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs @@ -2,115 +2,117 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using Roslyn.Core.Imaging; - using Roslyn.Text.Adornments; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Roslyn.Core.Imaging; +using Roslyn.Text.Adornments; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Object Content converter used to serialize and deserialize Text and Adornements from VS. +/// +/// This converts the following types: +/// +/// , +/// , +/// , +/// , +/// . +/// +/// Every other type is serialized as a string using the method. +/// +internal class ObjectContentConverter : JsonConverter +{ /// - /// Object Content converter used to serialize and deserialize Text and Adornements from VS. - /// - /// This converts the following types: - /// - /// , - /// , - /// , - /// , - /// . - /// - /// Every other type is serialized as a string using the method. + /// The property name used to save the .NET Type name of the serialized object. /// - internal class ObjectContentConverter : JsonConverter - { - /// - /// The property name used to save the .NET Type name of the serialized object. - /// - public const string TypeProperty = "_vs_type"; + public const string TypeProperty = "_vs_type"; - /// - /// A reusable instance of the . - /// - public static readonly ObjectContentConverter Instance = new ObjectContentConverter(); + /// + /// A reusable instance of the . + /// + public static readonly ObjectContentConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) + public override object? Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) { - return objectType == typeof(object); + reader.Read(); + return null; } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + else if (reader.TokenType == JsonTokenType.StartObject) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + var clonedReader = reader; + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) { - var data = JObject.Load(reader); - var type = data[TypeProperty]?.ToString() ?? throw new JsonSerializationException(); + var data = jsonDocument.RootElement; + var type = data.GetProperty(TypeProperty).GetString() ?? throw new JsonException(); - var tokenReader = data.CreateReader(); - tokenReader.Read(); switch (type) { case nameof(ImageId): - return ImageIdConverter.Instance.ReadJson(tokenReader, typeof(ImageId), null, serializer); + return ImageIdConverter.Instance.Read(ref clonedReader, typeof(ImageId), options); case nameof(ImageElement): - return ImageElementConverter.Instance.ReadJson(tokenReader, typeof(ImageElement), null, serializer); + return ImageElementConverter.Instance.Read(ref clonedReader, typeof(ImageElement), options); case nameof(ContainerElement): - return ContainerElementConverter.Instance.ReadJson(tokenReader, typeof(ContainerElementConverter), null, serializer); + return ContainerElementConverter.Instance.Read(ref clonedReader, typeof(ContainerElementConverter), options); case nameof(ClassifiedTextElement): - return ClassifiedTextElementConverter.Instance.ReadJson(tokenReader, typeof(ClassifiedTextElementConverter), null, serializer); + return ClassifiedTextElementConverter.Instance.Read(ref clonedReader, typeof(ClassifiedTextElementConverter), options); case nameof(ClassifiedTextRun): - return ClassifiedTextRunConverter.Instance.ReadJson(tokenReader, typeof(ClassifiedTextRunConverter), null, serializer); + return ClassifiedTextRunConverter.Instance.Read(ref clonedReader, typeof(ClassifiedTextRunConverter), options); default: return data; } } - else - { - return serializer.Deserialize(reader); - } } + else if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + else if (reader.TokenType == JsonTokenType.Number) + { + return reader.GetInt32(); + } + else + { + return JsonSerializer.Deserialize(ref reader, objectType, options); + } + } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + /// + public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) + { + if (value is null) { - if (value is null) - { - writer.WriteNull(); - return; - } + writer.WriteNullValue(); + return; + } - switch (value) - { - case ImageId: - ImageIdConverter.Instance.WriteJson(writer, value, serializer); - break; - case ImageElement: - ImageElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ContainerElement: - ContainerElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ClassifiedTextElement: - ClassifiedTextElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ClassifiedTextRun: - ClassifiedTextRunConverter.Instance.WriteJson(writer, value, serializer); - break; - default: - // According to the docs of ContainerElement point to https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.adornments.iviewelementfactoryservice - // which states that Editor supports ClassifiedTextElement, ContainerElement, ImageElement and that other objects would be presented using ToString unless an extender - // exports a IViewElementFactory for that type. So I will simply serialize unknown objects as strings. - writer.WriteValue(value.ToString()); - break; - } + switch (value) + { + case ImageId: + ImageIdConverter.Instance.Write(writer, (ImageId)value, options); + break; + case ImageElement: + ImageElementConverter.Instance.Write(writer, (ImageElement)value, options); + break; + case ContainerElement: + ContainerElementConverter.Instance.Write(writer, (ContainerElement)value, options); + break; + case ClassifiedTextElement: + ClassifiedTextElementConverter.Instance.Write(writer, (ClassifiedTextElement)value, options); + break; + case ClassifiedTextRun: + ClassifiedTextRunConverter.Instance.Write(writer, (ClassifiedTextRun)value, options); + break; + default: + // According to the docs of ContainerElement point to https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.adornments.iviewelementfactoryservice + // which states that Editor supports ClassifiedTextElement, ContainerElement, ImageElement and that other objects would be presented using ToString unless an extender + // exports a IViewElementFactory for that type. So I will simply serialize unknown objects as strings. + writer.WriteStringValue(value.ToString()); + break; } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs index 1bd71cd566617..881fa953b1fc1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs @@ -2,48 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Text.RegularExpressions; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; - using Newtonsoft.Json; +namespace Roslyn.LanguageServer.Protocol; - /// - /// Similar to https://devdiv.visualstudio.com/DevDiv/_git/VS-Platform?path=/src/Productivity/TextMate/Core/LanguageConfiguration/Impl/FastRegexConverter.cs - /// to allow us to only compile the regex option once. - /// - internal class RegexConverter : JsonConverter +/// +/// Similar to https://devdiv.visualstudio.com/DevDiv/_git/VS-Platform?path=/src/Productivity/TextMate/Core/LanguageConfiguration/Impl/FastRegexConverter.cs +/// to allow us to only compile the regex option once. +/// +internal class RegexConverter : JsonConverter +{ + public override Regex? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - { - // nameof is faster than typeof, so below is a fast path. - return objectType.Name == nameof(Regex) && objectType == typeof(Regex); - } - - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - // Create a custom deserializer for regex as the default provided by newtonsoft doesn't - // specify the Compiled option. - var regexText = reader.Value as string; - if (string.IsNullOrEmpty(regexText)) - { - return null; - } - - return new Regex(regexText, RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeout: TimeSpan.FromMilliseconds(1000)); - } + return new Regex(reader.GetString(), RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeout: TimeSpan.FromMilliseconds(1000)); + } - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is Regex valueAsRegex) - { - writer.WriteValue(valueAsRegex.ToString()); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {nameof(Regex)}"); - } - } + public override void Write(Utf8JsonWriter writer, Regex value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs index a1d482e7a480a..23dc024eca0ee 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs @@ -4,7 +4,9 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; + using System.Collections.Generic; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Utilities to aid work with VS Code LSP Extensions. @@ -15,37 +17,31 @@ internal static class VSCodeInternalExtensionUtilities /// Adds necessary to deserialize /// JSON stream into objects which include VS Code-specific extensions. /// - /// - /// If is used in parallel to execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that - /// collection is not modified when in use. - /// - /// Instance of which is guaranteed to not work in parallel to this method call. - public static void AddVSCodeInternalExtensionConverters(this JsonSerializer serializer) + public static void AddVSCodeInternalExtensionConverters(this JsonSerializerOptions options) { // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + var existingConvertersCount = options.Converters.Count; - AddOrReplaceConverter(); - AddOrReplaceConverter(); + AddOrReplaceConverter(options.Converters); + AddOrReplaceConverter(options.Converters); - void AddOrReplaceConverter() - where TExtension : TBase + void AddOrReplaceConverter(IList converters) + where TExtension : TBase { for (var i = 0; i < existingConvertersCount; i++) { - var existingConverterType = serializer.Converters[i].GetType(); + var existingConverterType = converters[i].GetType(); if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - serializer.Converters.RemoveAt(i); + converters.RemoveAt(i); existingConvertersCount--; break; } } - serializer.Converters.Add(new VSExtensionConverter()); + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs index dea7f8218f125..15e162c6c13eb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs @@ -2,72 +2,69 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Utilities to aid work with the LSP Extensions. +/// +internal static class VSInternalExtensionUtilities +{ /// - /// Utilities to aid work with the LSP Extensions. + /// Adds necessary to deserialize + /// JSON stream into objects which include VS-specific extensions. /// - internal static class VSInternalExtensionUtilities + internal static void AddVSInternalExtensionConverters(this JsonSerializerOptions options) { - /// - /// Adds necessary to deserialize - /// JSON stream into objects which include VS-specific extensions. - /// - /// - /// If is used in parallel to execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that - /// collection is not modified when in use. - /// - /// Instance of which is guaranteed to not work in parallel to this method call. - public static void AddVSInternalExtensionConverters(this JsonSerializer serializer) - { - VSExtensionUtilities.AddVSExtensionConverters(serializer); + VSExtensionUtilities.AddVSExtensionConverters(options); + AddConverters(options.Converters); + } - // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + private static void AddConverters(IList converters) + { + // Reading the number of converters before we start adding new ones + var existingConvertersCount = converters.Count; - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); - void AddOrReplaceConverter() - where TExtension : TBase + void AddOrReplaceConverter() + where TExtension : TBase + { + for (var i = 0; i < existingConvertersCount; i++) { - for (var i = 0; i < existingConvertersCount; i++) + var existingConverterType = converters[i].GetType(); + if (existingConverterType.IsGenericType && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && + existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - var existingConverterType = serializer.Converters[i].GetType(); - if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && - existingConverterType.GenericTypeArguments[0] == typeof(TBase)) - { - serializer.Converters.RemoveAt(i); - existingConvertersCount--; - break; - } + converters.RemoveAt(i); + existingConvertersCount--; + break; } - - serializer.Converters.Add(new VSExtensionConverter()); } + + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs index 2e2756573aa8e..3ac8099a356ec 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs @@ -5,14 +5,11 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of a diagnostic. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct VSInternalDiagnosticKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs index 5ae9280872f06..1bf9a1d635dc5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Server provided options for pull diagnostic requests. /// - [DataContract] internal record class VSInternalDiagnosticOptions : IWorkDoneProgressOptions { /// @@ -20,29 +18,29 @@ internal record class VSInternalDiagnosticOptions : IWorkDoneProgressOptions /// VS client will then use the information to do any merging logic in the Error List. /// Maps to . /// - [DataMember(Name = "_vs_buildOnlyDiagnosticIds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_buildOnlyDiagnosticIds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? BuildOnlyDiagnosticIds { get; init; } /// /// Gets or sets a list of diagnostic kinds used to query diagnostics in each context. /// - [DataMember(Name = "_vs_diagnosticKinds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticKinds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind[]? DiagnosticKinds { get; init; } /// /// Gets or sets a value indicating whether the server provides support for sending diagnostics requests for all project contexts. /// - [DataMember(Name = "_vs_supportsMultipleContextDiagnostics")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsMultipleContextDiagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsMultipleContextsDiagnostics { get; init; } /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "_vs_workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs index 40e808078218a..d45d258d1091c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request parameter used. /// - [DataContract] internal class VSInternalDiagnosticParams { /// /// Gets or sets the document for which diagnostics are desired. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier? TextDocument { get; set; } /// /// Gets or sets a value indicating what kind of diagnostic this request is querying for. /// - [DataMember(Name = "_vs_queryingDiagnosticKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_queryingDiagnosticKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind? QueryingDiagnosticKind { get; set; } /// @@ -46,8 +44,8 @@ internal class VSInternalDiagnosticParams /// document, then all reports are expected to have the same /// previousResultId. /// - [DataMember(Name = "_vs_previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs index 29b96efd3f442..71421912abc11 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs @@ -5,13 +5,11 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request report. /// - [DataContract] internal class VSInternalDiagnosticReport { /// @@ -21,8 +19,8 @@ internal class VSInternalDiagnosticReport /// diagnostics.The server can use this result ID to avoid resending /// diagnostics that had previously been sent. /// - [DataMember(Name = "_vs_resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// @@ -32,8 +30,8 @@ internal class VSInternalDiagnosticReport /// /// Is null if no changes in the diagnostics. Is empty if there is no diagnostic. /// - [DataMember(Name = "_vs_diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Diagnostic[]? Diagnostics { get; set; } /// @@ -43,8 +41,8 @@ internal class VSInternalDiagnosticReport /// entries tagged with will /// be hidden in the editor. /// - [DataMember(Name = "_vs_identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Identifier { get; set; } /// @@ -53,8 +51,8 @@ internal class VSInternalDiagnosticReport /// /// Diagnostics in a superseded report will be hidden if they have the PotentialDuplicate VSDiagnosticTag. /// - [DataMember(Name = "_vs_supersedes")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supersedes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Supersedes { get; set; } /// @@ -63,15 +61,15 @@ internal class VSInternalDiagnosticReport /// outputId and the (outputKey, outputId) uniquely identify /// a line of text in the output window). /// - [DataMember(Name = "_vs_outputKey")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_outputKey")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Guid? OutputKey { get; set; } /// /// Gets or sets the document version. /// - [DataMember(Name = "_vs_version")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_version")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Version { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs index 8b61f2fbf6438..2a136bb3fab29 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs @@ -5,27 +5,25 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request for a specific document. /// - [DataContract] internal class VSInternalDocumentDiagnosticsParams : VSInternalDiagnosticParams, IPartialResultParams { /// /// Gets or sets an optional token that a server can use to report work done progress. /// - [DataMember(Name = Methods.WorkDoneTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.WorkDoneTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? WorkDoneToken { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs index b4841e7c41664..a14f42cf55b4e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request result for all documents. /// - [DataContract] internal class VSInternalWorkspaceDiagnosticReport : VSInternalDiagnosticReport { /// /// Gets or sets the document for which diagnostics is returned. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier? TextDocument { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs index 1ddf9bd496914..61d145c73c8bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs @@ -5,41 +5,39 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request for all documents. /// - [DataContract] internal class VSInternalWorkspaceDiagnosticsParams : IPartialResultParams { /// /// Gets or sets the current state of the documents the client already has received. /// - [DataMember(Name = "_vs_previousResults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticParams[]? PreviousResults { get; set; } /// /// Gets or sets an optional token that a server can use to report work done progress. /// - [DataMember(Name = Methods.WorkDoneTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.WorkDoneTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? WorkDoneToken { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } /// /// Gets or sets a value indicating what kind of diagnostic this request is querying for. /// - [DataMember(Name = "_vs_queryingDiagnosticKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_queryingDiagnosticKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind? QueryingDiagnosticKind { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs index f99efeba0bbea..2c08c682897e0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A subclass of the VS LSP protocol extension that has a fast serialization path. /// - [DataContract] [JsonConverter(typeof(OptimizedVSCompletionListJsonConverter))] internal sealed class OptimizedVSCompletionList : VSInternalCompletionList { diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs index 2dbe0839d6d76..5288dde922493 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs @@ -2,272 +2,250 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using Roslyn.Core.Imaging; - using Newtonsoft.Json; - - internal class OptimizedVSCompletionListJsonConverter : JsonConverter - { - public static readonly OptimizedVSCompletionListJsonConverter Instance = new OptimizedVSCompletionListJsonConverter(); - private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Roslyn.Core.Imaging; - public override bool CanRead => false; +namespace Roslyn.LanguageServer.Protocol; - public override bool CanConvert(Type objectType) => typeof(OptimizedVSCompletionList) == objectType; +internal class OptimizedVSCompletionListJsonConverter : JsonConverter +{ + public static readonly OptimizedVSCompletionListJsonConverter Instance = new(); + private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException(); + public override OptimizedVSCompletionList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, OptimizedVSCompletionList value, JsonSerializerOptions options) + { + if (value is null) { - if (value is null) - { - writer.WriteNull(); - return; - } + writer.WriteNullValue(); + return; + } - var completionList = (VSInternalCompletionList)value; + var completionList = (VSInternalCompletionList)value; - writer.WriteStartObject(); + writer.WriteStartObject(); - if (completionList.SuggestionMode) - { - writer.WritePropertyName(VSInternalCompletionList.SuggestionModeSerializedName); - writer.WriteValue(completionList.SuggestionMode); - } - else - { - // Default is "false" so no need to serialize - } + if (completionList.SuggestionMode) + { + writer.WriteBoolean(VSInternalCompletionList.SuggestionModeSerializedName, completionList.SuggestionMode); + } + else + { + // Default is "false" so no need to serialize + } - if (completionList.ContinueCharacters != null && completionList.ContinueCharacters.Length > 0) - { - writer.WritePropertyName(VSInternalCompletionList.ContinueCharactersSerializedName); - serializer.Serialize(writer, completionList.ContinueCharacters); - } + if (completionList.ContinueCharacters != null && completionList.ContinueCharacters.Length > 0) + { + writer.WritePropertyName(VSInternalCompletionList.ContinueCharactersSerializedName); + JsonSerializer.Serialize(writer, completionList.ContinueCharacters, options); + } - if (completionList.Data != null) - { - writer.WritePropertyName(VSInternalCompletionList.DataSerializedName); - serializer.Serialize(writer, completionList.Data); - } + if (completionList.Data != null) + { + writer.WritePropertyName(VSInternalCompletionList.DataSerializedName); + JsonSerializer.Serialize(writer, completionList.Data, options); + } - if (completionList.CommitCharacters != null) - { - writer.WritePropertyName(VSInternalCompletionList.CommitCharactersSerializedName); - serializer.Serialize(writer, completionList.CommitCharacters); - } + if (completionList.CommitCharacters != null) + { + writer.WritePropertyName(VSInternalCompletionList.CommitCharactersSerializedName); + JsonSerializer.Serialize(writer, completionList.CommitCharacters, options); + } - if (completionList.IsIncomplete) - { - writer.WritePropertyName("isIncomplete"); - writer.WriteValue(completionList.IsIncomplete); - } - else - { - // Default is "false" so no need to serialize - } + if (completionList.IsIncomplete) + { + writer.WriteBoolean("isIncomplete", completionList.IsIncomplete); + } + else + { + // Default is "false" so no need to serialize + } - writer.WritePropertyName("items"); - if (completionList.Items == null || completionList.Items.Length == 0) - { - writer.WriteRawValue("[]"); - } - else - { - writer.WriteStartArray(); + writer.WritePropertyName("items"); + if (completionList.Items == null || completionList.Items.Length == 0) + { + writer.WriteRawValue("[]"); + } + else + { + writer.WriteStartArray(); - var itemRawJsonCache = new Dictionary(capacity: 1); + var itemRawJsonCache = new Dictionary(capacity: 1); - foreach (var completionItem in completionList.Items) + foreach (var completionItem in completionList.Items) + { + if (completionItem == null) { - if (completionItem == null) - { - continue; - } - - WriteCompletionItem(writer, completionItem, serializer, itemRawJsonCache); + continue; } - writer.WriteEndArray(); - } - - if (completionList.ItemDefaults != null) - { - writer.WritePropertyName("itemDefaults"); - serializer.Serialize(writer, completionList.ItemDefaults); + WriteCompletionItem(writer, completionItem, options, itemRawJsonCache); } - writer.WriteEndObject(); + writer.WriteEndArray(); } - private static void WriteCompletionItem(JsonWriter writer, CompletionItem completionItem, JsonSerializer serializer, Dictionary itemRawJsonCache) + if (completionList.ItemDefaults != null) { - writer.WriteStartObject(); + writer.WritePropertyName("itemDefaults"); + JsonSerializer.Serialize(writer, completionList.ItemDefaults, options); + } + + writer.WriteEndObject(); + } + + private static void WriteCompletionItem(Utf8JsonWriter writer, CompletionItem completionItem, JsonSerializerOptions options, Dictionary itemRawJsonCache) + { + writer.WriteStartObject(); - if (completionItem is VSInternalCompletionItem vsCompletionItem) + if (completionItem is VSInternalCompletionItem vsCompletionItem) + { + if (vsCompletionItem.Icon != null) { - if (vsCompletionItem.Icon != null) + if (!IconRawJson.TryGetValue(vsCompletionItem.Icon.ImageId, out var jsonString)) { - if (!IconRawJson.TryGetValue(vsCompletionItem.Icon.ImageId, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(vsCompletionItem.Icon, Formatting.None, ImageElementConverter.Instance); - IconRawJson.TryAdd(vsCompletionItem.Icon.ImageId, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.IconSerializedName); - writer.WriteRawValue(jsonString); + jsonString = JsonSerializer.Serialize(vsCompletionItem.Icon, options); + IconRawJson.TryAdd(vsCompletionItem.Icon.ImageId, jsonString); } + writer.WritePropertyName(VSInternalCompletionItem.IconSerializedName); + writer.WriteRawValue(jsonString); + } - if (vsCompletionItem.Description != null) - { - writer.WritePropertyName(VSInternalCompletionItem.DescriptionSerializedName); - ClassifiedTextElementConverter.Instance.WriteJson(writer, vsCompletionItem.Description, serializer); - } + if (vsCompletionItem.Description != null) + { + writer.WritePropertyName(VSInternalCompletionItem.DescriptionSerializedName); + JsonSerializer.Serialize(writer, vsCompletionItem.Description, options); + } - if (vsCompletionItem.VsCommitCharacters?.Value is string[] basicCommitCharacters - && basicCommitCharacters.Length > 0) - { - if (!itemRawJsonCache.TryGetValue(basicCommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(basicCommitCharacters); - itemRawJsonCache.Add(basicCommitCharacters, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); - writer.WriteRawValue(jsonString); - } - else if (vsCompletionItem.VsCommitCharacters?.Value is VSInternalCommitCharacter[] augmentedCommitCharacters - && augmentedCommitCharacters.Length > 0) + if (vsCompletionItem.VsCommitCharacters?.Value is string[] basicCommitCharacters + && basicCommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(basicCommitCharacters, out var jsonString)) { - if (!itemRawJsonCache.TryGetValue(augmentedCommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(augmentedCommitCharacters); - itemRawJsonCache.Add(augmentedCommitCharacters, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); - writer.WriteRawValue(jsonString); + jsonString = JsonSerializer.Serialize(basicCommitCharacters, options); + itemRawJsonCache.Add(basicCommitCharacters, jsonString); } - if (vsCompletionItem.VsResolveTextEditOnCommit) + writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); + writer.WriteRawValue(jsonString); + } + else if (vsCompletionItem.VsCommitCharacters?.Value is VSInternalCommitCharacter[] augmentedCommitCharacters + && augmentedCommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(augmentedCommitCharacters, out var jsonString)) { - writer.WritePropertyName(VSInternalCompletionItem.VsResolveTextEditOnCommitName); - writer.WriteValue(vsCompletionItem.VsResolveTextEditOnCommit); + jsonString = JsonSerializer.Serialize(augmentedCommitCharacters, options); + itemRawJsonCache.Add(augmentedCommitCharacters, jsonString); } - } - var label = completionItem.Label; - if (label != null) - { - writer.WritePropertyName("label"); - writer.WriteValue(label); + writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); + writer.WriteRawValue(jsonString); } - if (completionItem.LabelDetails != null) + if (vsCompletionItem.VsResolveTextEditOnCommit) { - writer.WritePropertyName("labelDetails"); - serializer.Serialize(writer, completionItem.LabelDetails); + writer.WriteBoolean(VSInternalCompletionItem.VsResolveTextEditOnCommitName, vsCompletionItem.VsResolveTextEditOnCommit); } + } - writer.WritePropertyName("kind"); - writer.WriteValue(completionItem.Kind); - - if (completionItem.Tags != null) - { - writer.WritePropertyName("tags"); - serializer.Serialize(writer, completionItem.Tags); - } + var label = completionItem.Label; + if (label != null) + { + writer.WriteString("label", label); + } - if (completionItem.Detail != null) - { - writer.WritePropertyName("detail"); - writer.WriteValue(completionItem.Detail); - } + if (completionItem.LabelDetails != null) + { + writer.WritePropertyName("labelDetails"); + JsonSerializer.Serialize(writer, completionItem.LabelDetails, options); + } - if (completionItem.Documentation != null) - { - writer.WritePropertyName("documentation"); - serializer.Serialize(writer, completionItem.Documentation); - } + writer.WriteNumber("kind", (int)completionItem.Kind); - // Only render preselect if it's "true" - if (completionItem.Preselect) - { - writer.WritePropertyName("preselect"); - writer.WriteValue(completionItem.Preselect); - } + if (completionItem.Detail != null) + { + writer.WriteString("detail", completionItem.Detail); + } - if (completionItem.SortText != null && !string.Equals(completionItem.SortText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("sortText"); - writer.WriteValue(completionItem.SortText); - } + if (completionItem.Documentation != null) + { + writer.WritePropertyName("documentation"); + JsonSerializer.Serialize(writer, completionItem.Documentation, options); + } - if (completionItem.FilterText != null && !string.Equals(completionItem.FilterText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("filterText"); - writer.WriteValue(completionItem.FilterText); - } + // Only render preselect if it's "true" + if (completionItem.Preselect) + { + writer.WriteBoolean("preselect", completionItem.Preselect); + } - if (completionItem.InsertText != null && !string.Equals(completionItem.InsertText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("insertText"); - writer.WriteValue(completionItem.InsertText); - } + if (completionItem.SortText != null && !string.Equals(completionItem.SortText, label, StringComparison.Ordinal)) + { + writer.WriteString("sortText", completionItem.SortText); + } - if (completionItem.InsertTextFormat != default && completionItem.InsertTextFormat != InsertTextFormat.Plaintext) - { - writer.WritePropertyName("insertTextFormat"); - writer.WriteValue(completionItem.InsertTextFormat); - } + if (completionItem.FilterText != null && !string.Equals(completionItem.FilterText, label, StringComparison.Ordinal)) + { + writer.WriteString("filterText", completionItem.FilterText); + } - if (completionItem.TextEdit != null) - { - writer.WritePropertyName("textEdit"); - serializer.Serialize(writer, completionItem.TextEdit); - } + if (completionItem.InsertText != null && !string.Equals(completionItem.InsertText, label, StringComparison.Ordinal)) + { + writer.WriteString("insertText", completionItem.InsertText); + } - if (completionItem.TextEditText != null) - { - writer.WritePropertyName("textEditText"); - serializer.Serialize(writer, completionItem.TextEditText); - } + if (completionItem.InsertTextFormat != default && completionItem.InsertTextFormat != InsertTextFormat.Plaintext) + { + writer.WriteNumber("insertTextFormat", (int)completionItem.InsertTextFormat); + } - if (completionItem.AdditionalTextEdits != null && completionItem.AdditionalTextEdits.Length > 0) - { - writer.WritePropertyName("additionalTextEdits"); - serializer.Serialize(writer, completionItem.AdditionalTextEdits); - } + if (completionItem.TextEdit != null) + { + writer.WritePropertyName("textEdit"); + JsonSerializer.Serialize(writer, completionItem.TextEdit, options); + } - if (completionItem.CommitCharacters != null && completionItem.CommitCharacters.Length > 0) - { - if (!itemRawJsonCache.TryGetValue(completionItem.CommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(completionItem.CommitCharacters); - itemRawJsonCache.Add(completionItem.CommitCharacters, jsonString); - } + if (completionItem.TextEditText != null) + { + writer.WritePropertyName("textEditText"); + JsonSerializer.Serialize(writer, completionItem.TextEditText, options); + } - writer.WritePropertyName("commitCharacters"); - writer.WriteRawValue(jsonString); - } + if (completionItem.AdditionalTextEdits != null && completionItem.AdditionalTextEdits.Length > 0) + { + writer.WritePropertyName("additionalTextEdits"); + JsonSerializer.Serialize(writer, completionItem.AdditionalTextEdits, options); + } - if (completionItem.Command != null) + if (completionItem.CommitCharacters != null && completionItem.CommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(completionItem.CommitCharacters, out var jsonString)) { - writer.WritePropertyName("command"); - serializer.Serialize(writer, completionItem.Command); + jsonString = JsonSerializer.Serialize(completionItem.CommitCharacters, options); + itemRawJsonCache.Add(completionItem.CommitCharacters, jsonString); } - if (completionItem.Data != null) - { - writer.WritePropertyName("data"); - serializer.Serialize(writer, completionItem.Data); - } + writer.WritePropertyName("commitCharacters"); + writer.WriteRawValue(jsonString); + } + + if (completionItem.Command != null) + { + writer.WritePropertyName("command"); + JsonSerializer.Serialize(writer, completionItem.Command, options); + } - writer.WriteEndObject(); + if (completionItem.Data != null) + { + writer.WritePropertyName("data"); + JsonSerializer.Serialize(writer, completionItem.Data, options); } + + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs index e472d496076d0..802b5a32ef129 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs @@ -5,10 +5,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Roslyn.Text.Adornments; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments { + [JsonConverter(typeof(ClassifiedTextElementConverter))] internal sealed class ClassifiedTextElement { public const string TextClassificationTypeName = "text"; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs index e79d24774d717..89d71bfad4538 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs @@ -3,9 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments; +[JsonConverter(typeof(ClassifiedTextRunConverter))] internal sealed class ClassifiedTextRun( string classificationTypeName, string text, diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs index c9f140776da0c..0d237c7b55ce7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs @@ -7,9 +7,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments { + [JsonConverter(typeof(ContainerElementConverter))] internal sealed class ContainerElement { public IEnumerable Elements { get; } @@ -28,4 +31,4 @@ public ContainerElement(ContainerElementStyle style, params object[] elements) Elements = elements?.ToImmutableList() ?? throw new ArgumentNullException("elements"); } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs index 441741d13ab6a..ec1918dcd7bcc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs @@ -25,4 +25,4 @@ internal enum ContainerElementStyle // Additional padding above and below content. VerticalPadding = 0x2 } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs index bbd213a10f8bd..4c818307b7d75 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs @@ -2,10 +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.Text.Json.Serialization; using Roslyn.Core.Imaging; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments; +[JsonConverter(typeof(ImageElementConverter))] internal sealed class ImageElement { public static readonly ImageElement Empty = new(default, string.Empty); @@ -17,6 +20,7 @@ public ImageElement(ImageId imageId) : this(imageId, null) { } + [JsonConstructor] public ImageElement(ImageId imageId, string? automationName) { ImageId = imageId; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs index 9b226ebb2938a..bfa412a7959ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs @@ -4,6 +4,8 @@ using System; using System.Globalization; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Core.Imaging { @@ -15,6 +17,7 @@ namespace Roslyn.Core.Imaging // On Windows systems, Microsoft.VisualStudio.Core.Imaging.ImageId can be converted // to and from various other image representations via the ImageIdExtensions extension // methods. + [JsonConverter(typeof(ImageIdConverter))] internal struct ImageId : IEquatable { // @@ -101,4 +104,4 @@ public override int GetHashCode() return hashCode ^ id.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs index a7d46e257f83e..a2ad7eb9fae76 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class used to extend to add internal capabilities. @@ -15,8 +14,8 @@ internal class VSFoldingRangeSetting : FoldingRangeSetting /// /// Gets or sets a value indicating whether if client only supports entire line folding only. /// - [DataMember(Name = "_vs_refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs index 476b1bbc3238e..ec9d0e7459dee 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for ClientCapabilities with fields specific to Visual Studio. /// - [DataContract] internal class VSInternalClientCapabilities : ClientCapabilities { /// /// Gets or sets a value indicating whether client supports Visual Studio extensions. /// - [DataMember(Name = "_vs_supportsVisualStudioExtensions")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsVisualStudioExtensions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsVisualStudioExtensions { get; @@ -28,8 +26,8 @@ public bool SupportsVisualStudioExtensions /// Gets or sets a value indicating what level of snippet support is available from Visual Studio Client. /// v1.0 refers to only default tab stop support i.e. support for $0 which manipualtes the cursor position. /// - [DataMember(Name = "_vs_supportedSnippetVersion")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supportedSnippetVersion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalSnippetSupportLevel? SupportedSnippetVersion { get; @@ -39,8 +37,8 @@ public VSInternalSnippetSupportLevel? SupportedSnippetVersion /// /// Gets or sets a value indicating whether client supports omitting document text in textDocument/didOpen notifications. /// - [DataMember(Name = "_vs_supportsNotIncludingTextInTextDocumentDidOpen")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsNotIncludingTextInTextDocumentDidOpen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsNotIncludingTextInTextDocumentDidOpen { get; @@ -51,8 +49,8 @@ public bool SupportsNotIncludingTextInTextDocumentDidOpen /// Gets or sets a value indicating whether the client supports string based response kinds /// instead of enum based response kinds. /// - [DataMember(Name = "_vs_supportsIconExtensions")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsIconExtensions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsIconExtensions { get; @@ -62,8 +60,8 @@ public bool SupportsIconExtensions /// /// Gets or sets a value indicating whether the client provides support for diagnostic pull requests. /// - [DataMember(Name = "_vs_supportsDiagnosticRequests")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsDiagnosticRequests")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsDiagnosticRequests { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs index eb25ceed3c90f..4fedc2a05ee70 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents content to be sent to the clipboard. /// - [DataContract] internal class VSInternalClipboardContent { /// /// Gets or sets a string that describes clipboard format types, for example, "text/plain". /// - [DataMember(Name = "_vs_mime", IsRequired = true)] + [JsonPropertyName("_vs_mime")] + [JsonRequired] public string MimeType { get; @@ -25,7 +25,8 @@ public string MimeType /// /// Gets or sets the content of the clipboard. /// - [DataMember(Name = "_vs_content", IsRequired = true)] + [JsonPropertyName("_vs_content")] + [JsonRequired] public string Content { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs index 2daba780549e6..5b44a7388b215 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class used to extend to add the data field for codeAction/_ms_resolve support. /// - [DataContract] internal class VSInternalCodeAction : CodeAction { /// /// Gets or sets the group this CodeAction belongs to. /// - [DataMember(Name = "_vs_group")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_group")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Group { get; @@ -28,8 +26,8 @@ public string? Group /// /// Gets or sets the priority level of the code action. /// - [DataMember(Name = "_vs_priority")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_priority")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalPriorityLevel? Priority { get; @@ -39,8 +37,8 @@ public VSInternalPriorityLevel? Priority /// /// Gets or sets the range of the span this action is applicable to. /// - [DataMember(Name = "_vs_applicableRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_applicableRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? ApplicableRange { get; @@ -50,8 +48,8 @@ public Range? ApplicableRange /// /// Gets or sets the children of this action. /// - [DataMember(Name = "_vs_children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCodeAction[]? Children { get; @@ -61,8 +59,8 @@ public VSInternalCodeAction[]? Children /// /// Gets or sets the telemetry id of this action. /// - [DataMember(Name = "_vs_telemetryId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_telemetryId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Guid? TelemetryId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs index 93a48ed25cbc6..a6384488e787f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for the textDocument/codeAction request. /// - [DataContract] internal class VSInternalCodeActionContext : CodeActionContext { /// /// Gets or sets the range of the current selection in the document for which the command was invoked. /// If there is no selection this would be a Zero-length range for the caret position. /// - [DataMember(Name = "_vs_selectionRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_selectionRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? SelectionRange { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs index 8bd608dbd27fe..87cdca79cd594 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class containing the set of code action default groups that are supported. /// - [DataContract] internal class VSInternalCodeActionGroupSetting { /// /// Gets or sets the code actions default group names the client supports. /// - [DataMember(Name = "_vs_valueSet")] + [JsonPropertyName("_vs_valueSet")] public string[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs index 58da67255fd31..86e2389eefff3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing support for code action literals. /// - [DataContract] internal class VSInternalCodeActionLiteralSetting : CodeActionLiteralSetting { /// /// Gets or sets a value indicating what code action default groups are supported. /// - [DataMember(Name = "_vs_codeActionGroup")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_codeActionGroup")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCodeActionGroupSetting? CodeActionGroup { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs index 9e9b8ab8cebf2..1333ec15adfb5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs @@ -4,24 +4,23 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Extension class for CompletionItem with fields specific to Visual Studio functionalities. /// - [DataContract] internal class VSInternalCommitCharacter { /// /// Gets or sets the commit character. /// - [DataMember(Name = "_vs_character")] + [JsonPropertyName("_vs_character")] public string Character { get; set; } /// /// Gets or sets a value indicating whether the commit character should be inserted or not. /// - [DataMember(Name = "_vs_insert")] + [JsonPropertyName("_vs_insert")] public bool Insert { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs index fc0b92a2fe216..3bcc6ea57ea39 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for with properties specific to Visual Studio. /// - [DataContract] internal class VSInternalCompletionContext : CompletionContext { /// /// Gets or sets the indicating how the completion was triggered. /// - [DataMember(Name = "_vs_invokeKind")] + [JsonPropertyName("_vs_invokeKind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(VSInternalCompletionInvokeKind.Explicit)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public VSInternalCompletionInvokeKind InvokeKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs index a8bd492e3ebd2..9bd3560beb600 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs @@ -4,13 +4,10 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Provides value for which specifies /// how completion was invoked. /// - [DataContract] internal enum VSInternalCompletionInvokeKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs index 7edcd8403e1ef..392f1a00bfac6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs @@ -4,14 +4,12 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for CompletionItem with fields specific to Visual Studio functionalities. /// - [DataContract] internal class VSInternalCompletionItem : CompletionItem { internal const string IconSerializedName = "_vs_icon"; @@ -22,17 +20,17 @@ internal class VSInternalCompletionItem : CompletionItem /// /// Gets or sets the icon to show for the completion item. In VS, this is more extensive than the completion kind. /// - [DataMember(Name = IconSerializedName)] + [JsonPropertyName(IconSerializedName)] [JsonConverter(typeof(ImageElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImageElement? Icon { get; set; } /// /// Gets or sets the description for a completion item. /// - [DataMember(Name = DescriptionSerializedName)] + [JsonPropertyName(DescriptionSerializedName)] [JsonConverter(typeof(ClassifiedTextElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ClassifiedTextElement? Description { get; set; } /// @@ -41,16 +39,16 @@ internal class VSInternalCompletionItem : CompletionItem /// If present, client will use this value instead of . /// If absent, client will default to . /// - [DataMember(Name = VsCommitCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(VsCommitCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? VsCommitCharacters { get; set; } /// /// Gets or sets a value indicating whether the client should call to /// get the value of the text edit to commit. /// - [DataMember(Name = VsResolveTextEditOnCommitName)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName(VsResolveTextEditOnCommitName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool VsResolveTextEditOnCommit { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs index e6984d8584a40..7a3fc2e1a3997 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A subclass of the LSP protocol that contains extensions specific to Visual Studio. /// - [DataContract] internal class VSInternalCompletionList : CompletionList { internal const string SuggestionModeSerializedName = "_vs_suggestionMode"; @@ -21,8 +19,8 @@ internal class VSInternalCompletionList : CompletionList /// /// Gets or sets a value indicating whether the completion list should use suggestion mode. In suggestion mode items are "soft-selected" by default. /// - [DataMember(Name = SuggestionModeSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(SuggestionModeSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SuggestionMode { get; @@ -32,8 +30,8 @@ public bool SuggestionMode /// /// Gets or sets the continue characters for the completion list. /// - [DataMember(Name = ContinueCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(ContinueCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType[]? ContinueCharacters { get; @@ -43,8 +41,8 @@ public SumType /// Gets or sets the default used for completion items. /// - [DataMember(Name = DataSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(DataSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; @@ -57,8 +55,8 @@ public object? Data /// /// If set, overrides . /// - [DataMember(Name = CommitCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(CommitCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? CommitCharacters { get; set; } // NOTE: Any changes that are added to this file may need to be reflected in its "optimized" counterparts JsonConverter (OptomizedVSCompletionListJsonConverter). diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs index 4f613cd4e478a..151d6c1c78bce 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion list. /// - [DataContract] internal class VSInternalCompletionListSetting { /// /// Gets or sets a value indicating whether completion lists can have Data bags. These data bags get propagated /// onto underlying completion items unless they have their own data bags. /// - [DataMember(Name = "_vs_data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Data { get; @@ -29,8 +27,8 @@ public bool Data /// Gets or sets a value indicating whether completion lists can have VSCommitCharacters. These commit characters get propagated /// onto underlying valid completion items unless they have their own commit characters. /// - [DataMember(Name = "_vs_commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CommitCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs index 41cd0d069d8cb..9496e3f1eecd2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for VS completion. /// - [DataContract] internal class VSInternalCompletionSetting : CompletionSetting { /// /// Gets or sets completion list setting. /// - [DataMember(Name = "_vs_completionList")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_completionList")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCompletionListSetting? CompletionList { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs index dbfacba1f2cf3..e59831f19eeae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a unicode character class for completion continuation. /// - [DataContract] internal class VSInternalContinueCharacterClass { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "unicodeClass"; /// /// Gets or sets the unicode class. /// - [DataMember(Name = "_vs_unicodeClass")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_unicodeClass")] + [JsonRequired] public string UnicodeClass { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs index 46bc21e6ba34f..d464790256715 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs @@ -4,34 +4,32 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing range of characters for completion continuation. /// - [DataContract] internal class VSInternalContinueCharacterRange { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "charRange"; /// /// Gets or sets the first completion character of the range. /// - [DataMember(Name = "_vs_start")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_start")] + [JsonRequired] public string Start { get; set; } /// /// Gets or sets the last completion character of the range. /// - [DataMember(Name = "_vs_end")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_end")] + [JsonRequired] public string End { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs index 233e1e82129e2..cb2af1a924f5d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing single continue character for completion. /// - [DataContract] internal class VSInternalContinueCharacterSingle { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "singleChar"; /// /// Gets or sets the completion character. /// - [DataMember(Name = "_vs_char")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_char")] + [JsonRequired] public string Character { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs index 785e96b9decad..378ff67e08581 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the options for on auto insert. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertOptions { /// /// Gets or sets trigger characters for on auto insert. /// - [DataMember(Name = "_vs_triggerCharacters")] + [JsonPropertyName("_vs_triggerCharacters")] public string[] TriggerCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs index 4fe5d9c1897a7..0000038032286 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_ms_onAutoInsert request. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertParams : ITextDocumentPositionParams { /// /// Gets or sets the representing the document to format. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -25,7 +24,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the at which the request was sent. /// - [DataMember(Name = "_vs_position")] + [JsonPropertyName("_vs_position")] public Position Position { get; @@ -35,7 +34,7 @@ public Position Position /// /// Gets or sets the character that was typed. /// - [DataMember(Name = "_vs_ch")] + [JsonPropertyName("_vs_ch")] public string Character { get; @@ -45,7 +44,7 @@ public string Character /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_options")] + [JsonPropertyName("_vs_options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs index d9b52602383c3..f196732392f86 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs @@ -6,20 +6,18 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of an AutoInsert response. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertResponseItem { /// /// Gets or sets the insert text format of the primary text edit. for supported formats. /// - [DataMember(Name = "_vs_textEditFormat")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_textEditFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [DefaultValue(InsertTextFormat.Plaintext)] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] @@ -32,8 +30,8 @@ public InsertTextFormat TextEditFormat /// /// Gets or sets the text edit. /// - [DataMember(Name = "_vs_textEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_textEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit TextEdit { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs index 2bee314d31fa6..e3634b57d5e98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs @@ -5,22 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameter for tD/_vs_spellCheckableRanges. /// - [DataContract] internal class VSInternalDocumentSpellCheckableParams : VSInternalStreamingParams, IPartialResultParams { /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs index ba2abd3e80a02..168d365a49115 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs @@ -4,12 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing settings for well-known Visual Studio's code action command. /// - [DataContract] internal class VSInternalExecuteCommandClientCapabilities : DynamicRegistrationSetting { /// @@ -31,7 +30,7 @@ public VSInternalExecuteCommandClientCapabilities(bool value) /// /// Gets or sets a set of well-known commands name the given VS-LSP client supports. /// - [DataMember(Name = "_vs_supportedCommands")] + [JsonPropertyName("_vs_supportedCommands")] public string[] SupportedCommands { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs index de87ad88193bc..54c83f2ebf742 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension to Hover which adds additional data for colorization. /// - [DataContract] internal class VSInternalHover : Hover { /// @@ -18,9 +16,9 @@ internal class VSInternalHover : Hover /// of objects from the Microsoft.VisualStudio.Text.Adornments namespace, /// such as ContainerElements, ClassifiedTextElements and ClassifiedTextRuns. /// - [DataMember(Name = "_vs_rawContent")] + [JsonPropertyName("_vs_rawContent")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? RawContent { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs index aaf0fe7a8517a..a46ad067f020a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Response class when asking server to resolve the rendering information of a string kind. /// - [DataContract] internal class VSInternalIconMapping : IEquatable { /// /// Gets or sets the ImageElements for a certain kind. /// - [DataMember(Name = "_vs_images")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_images")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSImageId[]? Images { get; @@ -28,8 +26,8 @@ public VSImageId[]? Images /// /// Gets or sets the tags for a certain kind. To be used in the absence of ImageIds. /// - [DataMember(Name = "_vs_tags")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? Tags { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs index a3ab7943d4821..18f1ae5dd0ff6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Context for inline completion request. /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L27. /// - [DataContract] internal class VSInternalInlineCompletionContext { /// /// Gets or sets how completion was triggered. /// - [DataMember(Name = "_vs_triggerKind")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_triggerKind")] + [JsonRequired] public VSInternalInlineCompletionTriggerKind TriggerKind { get; set; } = VSInternalInlineCompletionTriggerKind.Explicit; /// @@ -27,8 +24,8 @@ internal class VSInternalInlineCompletionContext /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L45. /// - [DataMember(Name = "_vs_selectedCompletionInfo")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_selectedCompletionInfo")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public VSInternalSelectedCompletionInfo? SelectedCompletionInfo { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs index 740c6799c26d9..0970e77332246 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs @@ -4,23 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A single inline completion item response. /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L78. /// - [DataContract] internal class VSInternalInlineCompletionItem { /// /// Gets or sets the text to replace the range with. /// - [DataMember(Name = "_vs_text")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_text")] + [JsonRequired] public string Text { get; set; } /// @@ -28,22 +25,22 @@ internal class VSInternalInlineCompletionItem /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L94. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Range? Range { get; set; } /// /// Gets or sets the command that is executed after inserting this completion item. /// - [DataMember(Name = "_vs_command")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Command? Command { get; set; } /// /// Gets or sets the format of the insert text. /// - [DataMember(Name = "_vs_insertTextFormat")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_insertTextFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public InsertTextFormat? TextFormat { get; set; } = InsertTextFormat.Plaintext; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs index 57c7957df661b..cccb4782656a5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Response for an inline completions request. /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L72. /// - [DataContract] internal class VSInternalInlineCompletionList { /// /// Gets or sets the inline completion items. /// - [DataMember(Name = "_vs_items")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_items")] + [JsonRequired] public VSInternalInlineCompletionItem[] Items { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs index b73012d5900e8..113e1d17b6fdf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using System.Text.RegularExpressions; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// The options for inline completion. /// - [DataContract] internal class VSInternalInlineCompletionOptions { /// /// Gets or sets a regex used by the client to determine when to ask the server for snippets. /// - [DataMember(Name = "_vs_pattern")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_pattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonConverter(typeof(RegexConverter))] public Regex Pattern { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs index a57c4f294d4db..5deb5763fcfc4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs @@ -4,42 +4,40 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// The request data for an inline completions request. /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L24. /// - [DataContract] internal class VSInternalInlineCompletionRequest : ITextDocumentParams { /// /// Gets or sets the text document. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the position where inline completions are being requested. /// - [DataMember(Name = "_vs_position")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_position")] + [JsonRequired] public Position Position { get; set; } /// /// Gets or sets the context for the inline completions request. /// - [DataMember(Name = "_vs_context")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_context")] + [JsonRequired] public VSInternalInlineCompletionContext Context { get; set; } /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_options")] + [JsonPropertyName("_vs_options")] public FormattingOptions Options { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs index 476a038b93d7f..0a0f4335811de 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs @@ -4,13 +4,10 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// How the inline completion request was triggered. /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L58. /// - [DataContract] internal enum VSInternalInlineCompletionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs index 6167f1ff419e3..9ad6c9c4e99ad 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs @@ -5,19 +5,17 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class that contains the base kind and modifiers used to describe a response item. /// - [DataContract] internal class VSInternalKindAndModifier : IEquatable { /// /// Gets or sets the ImageIds for a certain kind. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public string Kind { get; @@ -27,8 +25,8 @@ public string Kind /// /// Gets or sets the modifier of the kind. /// - [DataMember(Name = "_vs_modifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_modifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? Modifier { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs index 4c62c6fb74ec5..915dc06853f81 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs @@ -5,14 +5,12 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for . Used to relay reference text information with colorization. /// - [DataContract] internal class VSInternalLocation : VSLocation { private object? textValue = null; @@ -20,9 +18,9 @@ internal class VSInternalLocation : VSLocation /// /// Gets or sets the text value for a location reference. Must be of type or or or . /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Text { get diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs index 26394166fef4a..ab32d5981107d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs @@ -4,17 +4,15 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; - [DataContract] internal class VSInternalMapCodeMapping { /// /// Gets or sets identifier for the document the contents are supposed to be mapped into. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_textDocument")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextDocumentIdentifier? TextDocument { get; @@ -24,7 +22,7 @@ public TextDocumentIdentifier? TextDocument /// /// Gets or sets strings of code/text to map into TextDocument. /// - [DataMember(Name = "_vs_contents")] + [JsonPropertyName("_vs_contents")] public string[] Contents { get; @@ -36,8 +34,8 @@ public string[] Contents /// related classes (in other documents), viewport, etc. Earlier items should be considered /// higher priority. /// - [DataMember(Name = "_vs_focusLocations")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_focusLocations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Location[][]? FocusLocations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs index 6932c4d998bb2..e1460443c6612 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs @@ -4,19 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// LSP Params for textDocument/mapCode calls. /// - [DataContract] internal class VSInternalMapCodeParams { /// /// Set of code blocks, associated with documents and regions, to map. /// - [DataMember(Name = "_vs_mappings")] + [JsonPropertyName("_vs_mappings")] public VSInternalMapCodeMapping[] Mappings { get; @@ -27,8 +25,8 @@ public VSInternalMapCodeMapping[] Mappings /// Changes that should be applied to the workspace by the mapper before performing /// the mapping operation. /// - [DataMember(Name = "_vs_updates")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_updates")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEdit? Updates { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs index 4334c1b2aff26..647eb28637172 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class for a project context. /// - [DataContract] internal class VSInternalProjectContext : VSProjectContext, IEquatable { /// /// Gets or sets the string context kind of the project context. /// - [DataMember(Name = "_vs_vsKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_vsKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalKindAndModifier? VSKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs index cf551ec93172e..add3d0afb01b7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs @@ -5,14 +5,12 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents references information. /// - [DataContract] internal class VSInternalReferenceItem { private object? definitionTextValue = null; @@ -21,7 +19,8 @@ internal class VSInternalReferenceItem /// /// Gets or sets the reference id. /// - [DataMember(Name = "_vs_id", IsRequired = true)] + [JsonPropertyName("_vs_id")] + [JsonRequired] public int Id { get; @@ -31,7 +30,7 @@ public int Id /// /// Gets or sets the reference location. /// - [DataMember(Name = "_vs_location")] + [JsonPropertyName("_vs_location")] public Location Location { get; @@ -41,8 +40,8 @@ public Location Location /// /// Gets or sets the definition Id. /// - [DataMember(Name = "_vs_definitionId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_definitionId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? DefinitionId { get; @@ -57,9 +56,9 @@ public int? DefinitionId /// This element should colorize syntax, but should not contain highlighting, e.g. /// embedded within should not define . /// - [DataMember(Name = "_vs_definitionText")] + [JsonPropertyName("_vs_definitionText")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? DefinitionText { get @@ -84,7 +83,7 @@ public object? DefinitionText /// /// Gets or sets the resolution status. /// - [DataMember(Name = "_vs_resolutionStatus")] + [JsonPropertyName("_vs_resolutionStatus")] public VSInternalResolutionStatusKind ResolutionStatus { get; @@ -94,7 +93,7 @@ public VSInternalResolutionStatusKind ResolutionStatus /// /// Gets or sets the reference kind. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public VSInternalReferenceKind[] Kind { get; @@ -104,29 +103,29 @@ public VSInternalReferenceKind[] Kind /// /// Gets or sets the document name to be displayed to user when needed.This can be used in cases where URI doesn't have a user friendly file name or it is a remote URI. /// - [DataMember(Name = "_vs_documentName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_documentName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DocumentName { get; set; } /// /// Gets or sets the project name. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// /// Gets or sets the containing type. /// - [DataMember(Name = "_vs_containingType")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_containingType")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainingType { get; set; } /// /// Gets or sets the containing member. /// - [DataMember(Name = "_vs_containingMember")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_containingMember")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainingMember { get; set; } /// @@ -146,9 +145,9 @@ public VSInternalReferenceKind[] Kind /// "MarkerFormatDefinition/HighlightedDefinition" for definitions. /// /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Text { get @@ -173,23 +172,23 @@ public object? Text /// Gets or sets the text value for display path.This would be a friendly display name for scenarios where the actual path on disk may be confusing for users. /// This doesn't have to correspond to a real file path, but does need to be parsable by the various Path.GetFileName() methods. /// - [DataMember(Name = "_vs_displayPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_displayPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DisplayPath { get; set; } /// /// Gets or sets the origin of the item.The origin is used to filter remote results. /// - [DataMember(Name = "_vs_origin")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_origin")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalItemOrigin? Origin { get; set; } /// /// Gets or sets the icon to show for the definition header. /// - [DataMember(Name = "_vs_definitionIcon")] + [JsonPropertyName("_vs_definitionIcon")] [JsonConverter(typeof(ImageElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImageElement? DefinitionIcon { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs index b511340956f5c..28c2d6c76a120 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs @@ -4,8 +4,6 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Diagnostics.CodeAnalysis; - /// /// Enum which represents the various reference kinds. /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs index ef0a031bbb6dd..790b9ef24d951 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents extensions of passed as parameter of find reference requests. /// - [DataContract] internal class VSInternalReferenceParams : ReferenceParams { /// /// Gets or sets a value indicating the scope of returned items. /// - [DataMember(Name = "_vs_scope")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_scope")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalItemOrigin? Scope { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs index 4fe711ae387b5..6c7eeee8638bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the user configuration (as defined in ) for a rename request. /// - [DataContract] internal class VSInternalRenameOptionSelection { /// /// Gets or sets the name that identifies the option. /// - [DataMember(Name = "_vs_name")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_name")] + [JsonRequired] public string Name { get; @@ -27,8 +25,8 @@ public string Name /// /// Gets or sets a value indicating whether the user selected the option. /// - [DataMember(Name = "_vs_value")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_value")] + [JsonRequired] public bool Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs index 4614f862c3eb2..23e893e123907 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a renaming option for customizing the edit in the 'textDocument/rename' request. /// - [DataContract] internal class VSInternalRenameOptionSupport { /// /// Gets or sets the name that identifies the option. /// - [DataMember(Name = "_vs_name")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_name")] + [JsonRequired] public string Name { get; @@ -27,8 +25,8 @@ public string Name /// /// Gets or sets the user-facing option label. /// - [DataMember(Name = "_vs_label")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_label")] + [JsonRequired] public string Label { get; @@ -38,8 +36,8 @@ public string Label /// /// Gets or sets a value indicating whether the option has a default value of true. /// - [DataMember(Name = "_vs_default")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_default")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Default { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs index 969228507d1b8..2281272ecd8b8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters (together with extra VS-specific options) sent for the /// 'textDocument/rename' request. /// - [DataContract] internal class VSInternalRenameParams : RenameParams { /// /// Gets or sets the rename option values as selected by the user. /// - [DataMember(Name = "_vs_optionSelections")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_optionSelections")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalRenameOptionSelection[]? OptionSelections { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs index 092944a6dadae..4f144aaa2c8bd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request, /// together with extra VS-specific options. /// - [DataContract] internal class VSInternalRenameRange : RenameRange { /// /// Gets or sets the supported options for the rename request. /// - [DataMember(Name = "_vs_supportedOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supportedOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalRenameOptionSupport[]? SupportedOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs index 963cc264e0acf..dd2a72a6119be 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs @@ -4,43 +4,41 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Information about the selected completion item for . /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L48. /// - [DataContract] internal class VSInternalSelectedCompletionInfo { /// /// Gets or sets the range of the selected completion item. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Range Range { get; set; } /// /// Gets or sets the text of the selected completion item. /// - [DataMember(Name = "_vs_text")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_text")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Text { get; set; } /// /// Gets or sets the completion item kind of the selected completion item. /// - [DataMember(Name = "_vs_completionKind")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_completionKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemKind CompletionKind { get; set; } /// /// Gets or sets a value indicating whether the completion item is a snippet. /// - [DataMember(Name = "_vs_isSnippetText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_isSnippetText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IsSnippetText { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs index b8758f0f60161..b0c441bcd1a93 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for ServerCapabilities with fields specific to Visual Studio. /// - [DataContract] internal class VSInternalServerCapabilities : VSServerCapabilities { /// @@ -24,8 +20,8 @@ internal class VSInternalServerCapabilities : VSServerCapabilities /// This is provided to facilitate transition from in-proc to OOP for teams that /// currently own both a Language Server for Ctrl+Q and a GoTo provider. /// - [DataMember(Name = "_vs_disableGoToWorkspaceSymbols")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_disableGoToWorkspaceSymbols")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DisableGoToWorkspaceSymbols { get; @@ -35,8 +31,8 @@ public bool DisableGoToWorkspaceSymbols /// /// Gets or sets a value indicating whether document/_ms_references is supported. /// - [DataMember(Name = "_vs_ReferencesProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_ReferencesProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MSReferencesProvider { get; @@ -46,8 +42,8 @@ public bool MSReferencesProvider /// /// Gets or sets a value indicating whether the server supports OnAutoInsert. /// - [DataMember(Name = "_vs_onAutoInsertProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_onAutoInsertProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDocumentOnAutoInsertOptions? OnAutoInsertProvider { get; @@ -58,8 +54,8 @@ public VSInternalDocumentOnAutoInsertOptions? OnAutoInsertProvider /// Gets or sets a value indicating whether the server requires document text to be included in textDocument/didOpen notifications. /// /// This capability is not intended to be included into the official LSP, hence _ms_ prefix. - [DataMember(Name = "_vs_doNotIncludeTextInTextDocumentDidOpen")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_doNotIncludeTextInTextDocumentDidOpen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DoNotIncludeTextInTextDocumentDidOpen { get; @@ -69,8 +65,8 @@ public bool DoNotIncludeTextInTextDocumentDidOpen /// /// Gets or sets a value indicating whether the server provides support to resolve string based response kinds. /// - [DataMember(Name = "_vs_KindDescriptionResolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_KindDescriptionResolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool KindDescriptionResolveProvider { get; @@ -80,8 +76,8 @@ public bool KindDescriptionResolveProvider /// /// Gets or sets a value indicating whether the server provides support for diagnostic pull requests. /// - [DataMember(Name = "_vs_supportsDiagnosticRequests")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsDiagnosticRequests")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsDiagnosticRequests { get; @@ -91,8 +87,8 @@ public bool SupportsDiagnosticRequests /// /// Gets or sets server specified options for diagnostic pull requests. /// - [DataMember(Name = "_vs_diagnosticProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticOptions? DiagnosticProvider { get; @@ -102,8 +98,8 @@ public VSInternalDiagnosticOptions? DiagnosticProvider /// /// Gets or sets a value indicating whether the server provides support for inline completion requests. /// - [DataMember(Name = "_vs_inlineCompletionOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_inlineCompletionOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalInlineCompletionOptions? InlineCompletionOptions { get; @@ -113,8 +109,8 @@ public VSInternalInlineCompletionOptions? InlineCompletionOptions /// /// Gets or sets a value indicating whether the server provides support for spell checking. /// - [DataMember(Name = "_vs_spellCheckingProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_spellCheckingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SpellCheckingProvider { get; @@ -124,8 +120,8 @@ public bool SpellCheckingProvider /// /// Gets or sets a value indicating whether the server supports validating breakable ranges. /// - [DataMember(Name = "_vs_breakableRangeProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_breakableRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool BreakableRangeProvider { get; @@ -135,8 +131,8 @@ public bool BreakableRangeProvider /// /// Gets or sets a value indicating whether the server supports uri presentation. /// - [DataMember(Name = "_vs_uriPresentationProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_uriPresentationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool UriPresentationProvider { get; @@ -146,8 +142,8 @@ public bool UriPresentationProvider /// /// Gets or sets a value indicating whether the server supports text presentation. /// - [DataMember(Name = "_vs_textPresentationProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_textPresentationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool TextPresentationProvider { get; @@ -157,8 +153,8 @@ public bool TextPresentationProvider /// /// Gets or sets the value which indicates what support the server has for code mapping. /// - [DataMember(Name = "_vs_mapCodeProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_mapCodeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MapCodeProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs index 74ca607565159..375585cc5c934 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for signature help information which contains colorized label information. /// - [DataContract] internal class VSInternalSignatureInformation : SignatureInformation { /// /// Gets or sets the value representing the colorized label. /// - [DataMember(Name = "_vs_colorizedLabel")] + [JsonPropertyName("_vs_colorizedLabel")] [JsonConverter(typeof(ClassifiedTextElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ClassifiedTextElement? ColorizedLabel { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs index ca3264b7e8c30..c0acf2fc98f8b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Report of spell checkable ranges. /// - [DataContract] internal class VSInternalSpellCheckableRangeReport { /// @@ -21,8 +18,8 @@ internal class VSInternalSpellCheckableRangeReport /// spell checkable ranges. The server can use this result ID to avoid resending /// spell checkable ranges that had previously been sent. /// - [DataMember(Name = "_vs_resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// @@ -56,8 +53,8 @@ internal class VSInternalSpellCheckableRangeReport /// 5 // Span length /// ] /// - [DataMember(Name = "_vs_ranges")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_ranges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int[]? Ranges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs index 3b0e082213ebe..173f983b6b293 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a streaming pull request parameter used. /// /// TODO: Deprecate VSInternalDiagnosticParams.cs to use this merged param instead. /// - [DataContract] internal class VSInternalStreamingParams : ITextDocumentParams { /// /// Gets or sets the document for which the feature is being requested for. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } /// @@ -40,8 +39,8 @@ internal class VSInternalStreamingParams : ITextDocumentParams /// document, then all reports are expected to have the same /// previousResultId. /// - [DataMember(Name = "_vs_previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs index 7e241f9b91960..469c85e86b4a7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for SymbolInformation with fields specific to Visual Studio functionalities. @@ -13,14 +12,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// This is a temporary protocol and should not be used. /// - [DataContract] internal class VSInternalSymbolInformation : VSSymbolInformation { /// /// Gets or sets the string kind used for icons. /// - [DataMember(Name = "_vs_vsKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_vsKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalKindAndModifier? VSKind { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs index abc8b2b3749ed..3e445253a2244 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Text document capabilities specific to Visual Studio. /// - [DataContract] internal class VSInternalTextDocumentClientCapabilities : TextDocumentClientCapabilities { /// /// Gets or sets the setting which determines if on auto insert can be dynamically registered. /// - [DataMember(Name = "_vs_onAutoInsert")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_onAutoInsert")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? OnAutoInsert { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs index e73b246d01f2e..933950179ae0a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// / Class representing the options for registering textDocument/_vs_OnAutoInsert support. /// - [DataContract] internal class VSInternalTextDocumentRegistrationOptions : TextDocumentRegistrationOptions { /// /// Gets or sets trigger characters for on auto insert. /// - [DataMember(Name = "_vs_triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs index 3ec280d525972..e2cd66192521e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_vs_textPresentation request. /// - [DataContract] internal class VSInternalTextPresentationParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be operate on. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; @@ -27,8 +25,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_range")] + [JsonRequired] public Range Range { get; @@ -38,7 +36,7 @@ public Range Range /// /// Gets or sets the text. /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] public string? Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs index 3736b48506e1e..99fa2ac931edf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs @@ -5,20 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_vs_uriPresentation request. /// - [DataContract] internal class VSInternalUriPresentationParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be operate on. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; @@ -28,8 +27,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_range")] + [JsonRequired] public Range Range { get; @@ -39,8 +38,7 @@ public Range Range /// /// Gets or sets the URI values. Valid for DropKind.Uris. /// - [DataMember(Name = "_vs_uris")] - [JsonProperty(ItemConverterType = typeof(DocumentUriConverter))] + [JsonPropertyName("_vs_uris")] public Uri[]? Uris { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs index f9a4464b2e282..e216a5b4aed8f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs @@ -4,24 +4,23 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the textDocument/validateBreakableRange request. /// - [DataContract] internal class VSInternalValidateBreakableRangeParams : ITextDocumentParams { /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the at which the request was sent. /// - [DataMember(Name = "_vs_range")] + [JsonPropertyName("_vs_range")] public Range Range { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs index 516eb1c8218ea..417638b7bd55d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs @@ -5,29 +5,25 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameter for workspace/_vs_spellCheckableRanges. /// - [DataContract] internal class VSInternalWorkspaceSpellCheckableParams : IPartialResultParams { /// /// Gets or sets the current state of the documents the client already has received. /// - [DataMember(Name = "_vs_previousResults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalStreamingParams[]? PreviousResults { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = "_vs_partialResultToken")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_partialResultToken")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs index a844892b092b1..5bfce67da8ca2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs @@ -2,23 +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.Text.Json.Serialization; + namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - /// /// Report for workspace spell checkable range request. /// - [DataContract] internal class VSInternalWorkspaceSpellCheckableReport : VSInternalSpellCheckableRangeReport, ITextDocumentParams { /// /// Gets or sets the document for which the spell checkable ranges are returned. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs index 4ad14537b3856..16d07390197b3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents linked editing range capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRangeOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs index d24bfd158ddd3..78cbad670a95f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Class representing the parameters sent for a textDocument/linkedEditingRange request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRangeParams : TextDocumentPositionParams { } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs index 9c0b78fd5c768..a60978b1e0cb9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of an LinkedEditingRanges response. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRanges { /// /// Gets or sets the ranges for the type rename. /// - [DataMember(Name = "ranges")] + [JsonPropertyName("ranges")] public Range[] Ranges { get; @@ -28,8 +26,8 @@ public Range[] Ranges /// /// Gets or sets the word pattern for the type rename. /// - [DataMember(Name = "wordPattern")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("wordPattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? WordPattern { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Location.cs b/src/Features/LanguageServer/Protocol/Protocol/Location.cs index e91082e4441dd..123dfea04440a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Location.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Location.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a location in a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Location : IEquatable { /// /// Gets or sets the URI for the document the location belongs to. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -31,7 +29,7 @@ public Uri Uri /// /// Gets or sets the range of the location in the document. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs b/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs index 8d80b156e99a5..6918677cc3014 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/logMessage requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LogMessageParams { /// /// Gets or sets the type of message. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public MessageType MessageType { get; @@ -27,7 +26,7 @@ public MessageType MessageType /// /// Gets or sets the message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs index 62c6b0d8a16d1..3ca516d0589ab 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing human readable text that should be rendered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MarkedString { /// /// Gets or sets the language of the code stored in . /// - [DataMember(Name = "language")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("language")] + [JsonRequired] public string Language { get; @@ -29,8 +27,8 @@ public string Language /// /// Gets or sets the code. /// - [DataMember(Name = "value")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("value")] + [JsonRequired] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs index ce92eda072808..de670049566fc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing text and an associated format that should be rendered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MarkupContent { /// /// Gets or sets the representing the text's format. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public MarkupKind Kind { get; @@ -27,7 +26,7 @@ public MarkupKind Kind /// /// Gets or sets the text that should be rendered. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs index 29dda1ba997c7..e4ec736ad7694 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the various formats of markup text. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct MarkupKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs index 4dc981f6f1827..8f4739241c9e2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represent an action the user performs after a window/showMessageRequest request is sent. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MessageActionItem { /// /// Gets or sets the title. /// - [DataMember(Name = "title")] + [JsonPropertyName("title")] public string Title { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs b/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs index 3b243d8a2f55f..b36f16e24c9d4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Message type enum. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum MessageType { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs index d34f9a1010fd7..32004eb595da6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Globalization; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document, but optionally has a version identifier. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class OptionalVersionedTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the version of the document. /// - [DataMember(Name = "version")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs index 38327189dcf73..e92aeb737af31 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a parameter of a callable signature. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(ParameterInformationConverter))] internal class ParameterInformation { /// /// Gets or sets the label of the parameter. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public SumType> Label { get; @@ -30,8 +28,8 @@ public SumType> Label /// /// Gets or sets the human-readable documentation of the parameter. /// - [DataMember(Name = "documentation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Documentation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs index debdaa4cdcbd0..a392f4102186d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameter information initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ParameterInformationSetting { /// /// Gets or sets a value indicating whether the client supports label offset. /// - [DataMember(Name = "labelOffsetSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelOffsetSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelOffsetSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Position.cs b/src/Features/LanguageServer/Protocol/Protocol/Position.cs index ec4a102e561be..a4a770efad361 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Position.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Position.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a position on a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Position : IEquatable { /// @@ -37,7 +35,7 @@ public Position(int line, int character) /// /// Gets or sets the line number. /// - [DataMember(Name = "line")] + [JsonPropertyName("line")] public int Line { get; @@ -47,7 +45,7 @@ public int Line /// /// Gets or sets the character number. /// - [DataMember(Name = "character")] + [JsonPropertyName("character")] public int Character { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs index 73184699a73db..2ef5c34b6eb05 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the 'textDocument/prepare' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PrepareRenameParams : ITextDocumentPositionParams { /// /// Gets or sets the value which identifies the document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the position in which the rename is requested. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs b/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs index 90dfe145b7c39..fb4a1319a87e1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the default behavior used by the client for computing a rename range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum PrepareSupportDefaultBehavior { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs b/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs index 59026f13ff01c..e8ae0c7e4e6bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a previous result id in a workspace pull request. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class PreviousResultId { /// /// Gets or sets the URI for which the client knows a result id. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the value of the previous result id. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs index c8cdae892ad2e..5b759a01c9aa3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that's sent with 'textDocument/publishDiagnostics' messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PublishDiagnosticParams { /// /// Gets or sets the URI of the text document. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the collection of diagnostics. /// - [DataMember(Name = "diagnostics")] + [JsonPropertyName("diagnostics")] public Diagnostic[] Diagnostics { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs index 044cc92b08910..63f44c94aabbf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the initialization setting for publish diagnostics. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PublishDiagnosticsSetting { /// /// Gets or sets a value indicating whether gets or sets the capabilities. /// - [DataMember(Name = "tagSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tagSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TagSupport? TagSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Range.cs b/src/Features/LanguageServer/Protocol/Protocol/Range.cs index 47284808a2d33..a1e423608df98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Range.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Range.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document text range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Range : IEquatable { /// /// Gets or sets the text start position. /// - [DataMember(Name = "start")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("start")] + [JsonRequired] public Position Start { get; @@ -31,8 +29,8 @@ public Position Start /// /// Gets or sets the text end position. /// - [DataMember(Name = "end")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("end")] + [JsonRequired] public Position End { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs index 36703524d53bd..2e7411b4411a0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing reference context information for find reference request parameter. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceContext { /// /// Gets or sets a value indicating whether declaration should be included. /// - [DataMember(Name = "includeDeclaration")] + [JsonPropertyName("includeDeclaration")] public bool IncludeDeclaration { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs index a65a49c2b9046..2282473292523 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs index 0809067d7a9ef..00a2106a75ddc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing find reference parameter for find reference request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceParams : TextDocumentPositionParams, IPartialResultParams { // Using IPartialResultParams instead of IPartialResultParams to @@ -22,7 +20,7 @@ internal class ReferenceParams : TextDocumentPositionParams, IPartialResultParam /// /// Gets or sets the reference context. /// - [DataMember(Name = "context")] + [JsonPropertyName("context")] public ReferenceContext Context { get; @@ -32,8 +30,8 @@ public ReferenceContext Context /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Registration.cs b/src/Features/LanguageServer/Protocol/Protocol/Registration.cs index fcf186fb298ff..906b4f4277d8e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Registration.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Registration.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the general registration information for registering for a capability. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Registration { /// /// Gets or sets the id used to register the request. This can be used to deregister later. /// - [DataMember(Name = "id")] + [JsonPropertyName("id")] public string Id { get; @@ -28,7 +26,7 @@ public string Id /// /// Gets or sets the method / capability to register for. /// - [DataMember(Name = "method")] + [JsonPropertyName("method")] public string Method { get; @@ -38,8 +36,8 @@ public string Method /// /// Gets or sets the options necessary for registration. /// - [DataMember(Name = "registerOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("registerOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? RegisterOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs index 0a212c4740440..4125036ab0e1f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the client/registerCapability request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RegistrationParams { /// /// Gets or sets the set of capabilities that are being registered. /// - [DataMember(Name = "registrations")] + [JsonPropertyName("registrations")] public Registration[] Registrations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs index 6c672b19efdaf..7b6fbc77a4210 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a full diagnostic report with a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class RelatedFullDocumentDiagnosticReport : FullDocumentDiagnosticReport { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedDocuments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary>? RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs index b4f1c2949d759..b9e968a0f9eda 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing an unchanged diagnostic report with a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class RelatedUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedDocuments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary>? RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs index df1fc09d55b7f..bea10606988cb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs @@ -4,25 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using System.Xml.Linq; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - using static System.Net.Mime.MediaTypeNames; + using System.Text.Json.Serialization; /// /// Class which represents renaming client capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameClientCapabilities : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports testing for validity of rename operations before execution. /// - [DataMember(Name = "prepareSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("prepareSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PrepareSupport { get; @@ -33,8 +28,8 @@ public bool PrepareSupport /// Gets or sets the value indicating the default behavior used by the client when the (`{ defaultBehavior: boolean }`) /// result is used in the 'textDocument/prepareRename' request. /// - [DataMember(Name = "prepareSupportDefaultBehavior")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("prepareSupportDefaultBehavior")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PrepareSupportDefaultBehavior? PrepareSupportDefaultBehavior { get; @@ -46,8 +41,8 @@ public PrepareSupportDefaultBehavior? PrepareSupportDefaultBehavior /// operations returned via the rename request's workspace edit, by for example presenting the workspace edit in /// the user interface and asking for confirmation. /// - [DataMember(Name = "honorsChangeAnnotations")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("honorsChangeAnnotations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool HonorsChangeAnnotations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs index 9e7a366ef345a..969e8a039d867 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a rename file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("rename")] internal class RenameFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "rename"; /// /// Gets or sets the old (existing) location. /// - [DataMember(Name = "oldUri", IsRequired = true)] + [JsonPropertyName("oldUri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri OldUri { @@ -38,7 +37,8 @@ public Uri OldUri /// /// Gets or sets the new location. /// - [DataMember(Name = "newUri", IsRequired = true)] + [JsonPropertyName("newUri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri NewUri { @@ -49,8 +49,8 @@ public Uri NewUri /// /// Gets or sets the rename options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RenameFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs index 64af96d73853a..59d45c42a225b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameFileOptions { /// /// Gets or sets a value indicating whether the rename should overwrite the target if it already exists. (Overwrite wins over ignoreIfExists). /// - [DataMember(Name = "overwrite")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overwrite")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Overwrite { get; @@ -29,8 +27,8 @@ public bool Overwrite /// /// Gets or sets a value indicating whether the action should be ignored if the file already exists. /// - [DataMember(Name = "ignoreIfExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs index f869eea186238..841b679e2e005 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the rename options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether renames should be checked and tested before being executed. /// - [DataMember(Name = "prepareProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("prepareProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PrepareProvider { get; @@ -29,8 +27,8 @@ public bool PrepareProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs index 29b85b249c9a3..3c95b96d9393e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the rename parameters for the textDocument/rename request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameParams : TextDocumentPositionParams { /// /// Gets or sets the new name of the renamed symbol. /// - [DataMember(Name = "newName")] + [JsonPropertyName("newName")] public string NewName { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs index f424a260e236c..984b50dc823a0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameRange { /// /// Gets or sets the range of the string to rename. /// - [DataMember(Name = "range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -29,8 +27,8 @@ public Range Range /// /// Gets or sets the placeholder text of the string content to be renamed. /// - [DataMember(Name = "placeholder")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("placeholder")] + [JsonRequired] public string Placeholder { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs index aa05c8d8e28ed..6ff560bb95808 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for properties a client can resolve lazily on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties", IsRequired = true)] + [JsonPropertyName("properties")] + [JsonRequired] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs b/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs index 1b56ff4510eaf..228b44234bf60 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of resource operations supported by the client. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct ResourceOperationKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs index e14887dc310ef..9d04b661df742 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents save option configurations. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SaveOptions { /// /// Gets or sets a value indicating whether clients include text content on save. /// - [DataMember(Name = "includeText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("includeText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IncludeText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs index 3b46370ef84c7..5a75e425e946b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the format used to describe semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct SemanticTokenFormat(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs index 5b1c243765f2a..bd467fbf475d6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs @@ -4,28 +4,27 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing response to semantic tokens messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokens { /// /// Gets or sets a property that identifies this version of the document's semantic tokens. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// /// Gets or sets and array containing encoded semantic tokens data. /// - [DataMember(Name = "data", IsRequired = true)] + [JsonPropertyName("data")] + [JsonRequired] public int[] Data { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs index c67e0d1e61523..f1697e0ac4939 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs @@ -4,31 +4,29 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents a response from a semantic tokens Document provider Edits request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDelta { /// /// Gets or sets the Id for the client's new version after applying all /// edits to their current semantic tokens data. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// /// Gets or sets an array of edits to apply to a previous response from a /// semantic tokens Document provider. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public SemanticTokensEdit[] Edits { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs index eee3588bb8a02..aded0c9dd957b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameters for a request for Edits that can be applied to a previous response @@ -14,27 +13,26 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDeltaParams : ITextDocumentParams, IPartialResultParams { /// /// Gets or sets an identifier for the document to fetch semantic tokens from. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets a property indicating the version of the semantic /// tokens Document provider response that the edits will be applied to. /// - [DataMember(Name = "previousResultId")] + [JsonPropertyName("previousResultId")] public string PreviousResultId { get; set; } /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs index 5905b06a385ab..dadc783ce447f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents a response from a semantic tokens Document provider Edits request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDeltaPartialResult { /// /// Gets or sets an array of edits to apply to a previous response from a /// semantic tokens Document provider. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public SemanticTokensEdit[] Edits { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs index 9958a6d858b5c..c06932c59a9f7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing an individual edit incrementally applied to a previous @@ -14,7 +13,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1036:Override methods on comparable types", Justification = "Pending implementation of IComparable")] internal class SemanticTokensEdit : IComparable { @@ -22,22 +20,22 @@ internal class SemanticTokensEdit : IComparable /// Gets or sets the position in the previous response's /// to begin the edit. /// - [DataMember(Name = "start")] + [JsonPropertyName("start")] public int Start { get; set; } /// /// Gets or sets the number of numbers to delete in the /// from the previous response. /// - [DataMember(Name = "deleteCount")] + [JsonPropertyName("deleteCount")] public int DeleteCount { get; set; } /// /// Gets or sets an array containing the encoded semantic tokens information to insert /// into a previous response. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int[]? Data { get; set; } /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs index ac04122c1dd4e..ff7dcdf3e4970 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Options for the full document semantic tokens classification provider. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensFullOptions { /// /// Gets or sets a value indicating whether the server supports deltas for full documents. /// - [DataMember(Name = "delta")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("delta")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Delta { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs index 1e2deb490d8b4..394c0d45c8f68 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Legend used to encode semantic token types in . /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensLegend { /// /// Gets or sets an array of token types that can be encoded in semantic tokens responses. /// - [DataMember(Name = "tokenTypes")] + [JsonPropertyName("tokenTypes")] public string[] TokenTypes { get; @@ -27,7 +26,7 @@ public string[] TokenTypes /// /// Gets or sets an array of token modfiers that can be encoded in semantic tokens responses. /// - [DataMember(Name = "tokenModifiers")] + [JsonPropertyName("tokenModifiers")] public string[] TokenModifiers { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs index 99e483f5ecb47..30fce0f5efaa6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs @@ -4,42 +4,40 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Initialization options for semantic tokens support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensOptions : IWorkDoneProgressOptions { /// /// Gets or sets a legend describing how semantic token types and modifiers are encoded in responses. /// - [DataMember(Name = "legend")] + [JsonPropertyName("legend")] public SemanticTokensLegend Legend { get; set; } /// /// Gets or sets a value indicating whether semantic tokens Range provider requests are supported. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Range { get; set; } /// /// Gets or sets whether or not the server supports providing semantic tokens for a full document. /// - [DataMember(Name = "full")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("full")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Full { get; set; } /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs index f388365622b58..5cf3738c12b6b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs @@ -5,28 +5,26 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameters for semantic tokens full Document request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensParams : ITextDocumentParams, IPartialResultParams { /// /// Gets or sets an identifier for the document to fetch semantic tokens from. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs index fef2fa0b35b48..3307b392e936a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing response to semantic tokens messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensPartialResult { /// /// Gets or sets and array containing encoded semantic tokens data. /// - [DataMember(Name = "data", IsRequired = true)] + [JsonPropertyName("data")] + [JsonRequired] public int[] Data { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs index b99746321df5b..0523c2e25293f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Parameters for the semantic tokens Range request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRangeParams : SemanticTokensParams { /// /// Gets or sets the range within the document to fetch semantic tokens for. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs index 60311963550f3..ce6ad7de1e5d6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Client settings for semantic tokens related to the @@ -13,7 +12,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRequestsFullSetting { /// @@ -21,8 +19,8 @@ internal class SemanticTokensRequestsFullSetting /// textDocument/semanticTokens/full/delta request if the server /// provides a corresponding handler. /// - [DataMember(Name = "range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Delta { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs index 26884ccc267b2..7c9dbd73769b9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Requests client settings for semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRequestsSetting { /// @@ -20,8 +18,8 @@ internal class SemanticTokensRequestsSetting /// `textDocument/semanticTokens/range` request if the server provides a /// corresponding handler. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Range { get; set; } /// @@ -29,8 +27,8 @@ internal class SemanticTokensRequestsSetting /// `textDocument/semanticTokens/full` request if the server provides a /// corresponding handler. /// - [DataMember(Name = "full")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("full")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Full { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs index ddef8c880b076..85e34d5680f9f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs @@ -4,56 +4,54 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Client settings for semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating which requests the client supports and might send to the server /// depending on the server's capability. /// - [DataMember(Name = "requests")] + [JsonPropertyName("requests")] public SemanticTokensRequestsSetting Requests { get; set; } /// /// Gets or sets an array of token types supported by the client for encoding /// semantic tokens. /// - [DataMember(Name = "tokenTypes")] + [JsonPropertyName("tokenTypes")] public string[] TokenTypes { get; set; } /// /// Gets or sets an array of token modifiers supported by the client for encoding /// semantic tokens. /// - [DataMember(Name = "tokenModifiers")] + [JsonPropertyName("tokenModifiers")] public string[] TokenModifiers { get; set; } /// /// Gets or sets an array of formats the clients supports. /// - [DataMember(Name = "formats")] + [JsonPropertyName("formats")] public SemanticTokenFormat[] Formats { get; set; } /// /// Gets or sets a value indicating whether the client supports tokens that can overlap each other. /// - [DataMember(Name = "overlappingTokenSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overlappingTokenSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool OverlappingTokenSupport { get; set; } /// /// Gets or sets a value indicating whether the client supports tokens that can span multiple lines. /// - [DataMember(Name = "multilineTokenSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("multilineTokenSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MultilineTokenSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs index dd101255ae22e..d560f44d6c5c2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Capabilities specific to the semantic token requests scoped to the workspace. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensWorkspaceSetting { /// @@ -25,8 +23,8 @@ internal class SemanticTokensWorkspaceSetting /// and is useful for situation where a server for example detect a project /// wide change that requires such a calculation. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs index 8e7416165f35f..840da00bb13b1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ServerCapabilities { /// /// Gets or sets the value which indicates how text document are synced. /// - [DataMember(Name = "textDocumentSync")] + [JsonPropertyName("textDocumentSync")] [JsonConverter(typeof(TextDocumentSyncConverter))] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] @@ -40,8 +38,8 @@ public TextDocumentSyncOptions? TextDocumentSync /// /// Gets or sets the value which indicates if completions are supported. /// - [DataMember(Name = "completionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionOptions? CompletionProvider { get; @@ -51,8 +49,8 @@ public CompletionOptions? CompletionProvider /// /// Gets or sets a value indicating whether the server provides hover support. /// - [DataMember(Name = "hoverProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("hoverProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? HoverProvider { get; @@ -62,8 +60,8 @@ public SumType? HoverProvider /// /// Gets or sets the value which indicates if signature help is supported. /// - [DataMember(Name = "signatureHelpProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureHelpProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpOptions? SignatureHelpProvider { get; @@ -73,8 +71,8 @@ public SignatureHelpOptions? SignatureHelpProvider /// /// Gets or sets a value indicating whether go to definition is supported. /// - [DataMember(Name = "definitionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("definitionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DefinitionProvider { get; @@ -84,8 +82,8 @@ public SumType? DefinitionProvider /// /// Gets or sets a value indicating whether go to type definition is supported. /// - [DataMember(Name = "typeDefinitionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("typeDefinitionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? TypeDefinitionProvider { get; @@ -95,8 +93,8 @@ public SumType? TypeDefinitionProvider /// /// Gets or sets a value indicating whether go to implementation is supported. /// - [DataMember(Name = "implementationProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("implementationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ImplementationProvider { get; @@ -106,8 +104,8 @@ public SumType? ImplementationProvider /// /// Gets or sets a value indicating whether find all references is supported. /// - [DataMember(Name = "referencesProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("referencesProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ReferencesProvider { get; @@ -117,8 +115,8 @@ public SumType? ReferencesProvider /// /// Gets or sets a value indicating whether the server supports document highlight. /// - [DataMember(Name = "documentHighlightProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentHighlightProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentHighlightProvider { get; @@ -128,8 +126,8 @@ public SumType? DocumentHighlightProvider /// /// Gets or sets a value indicating whether document symbols are supported. /// - [DataMember(Name = "documentSymbolProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentSymbolProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentSymbolProvider { get; @@ -139,8 +137,8 @@ public SumType? DocumentSymbolProvider /// /// Gets or sets a value indicating whether code actions are supported. /// - [DataMember(Name = "codeActionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? CodeActionProvider { get; @@ -150,8 +148,8 @@ public SumType? CodeActionProvider /// /// Gets or sets the value which indicates if code lens is supported. /// - [DataMember(Name = "codeLensProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLensProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeLensOptions? CodeLensProvider { get; @@ -161,8 +159,8 @@ public CodeLensOptions? CodeLensProvider /// /// Gets or sets the value which indicates if document link is supported. /// - [DataMember(Name = "documentLinkProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentLinkProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentLinkOptions? DocumentLinkProvider { get; @@ -172,8 +170,8 @@ public DocumentLinkOptions? DocumentLinkProvider /// /// Gets or sets the value which indicates if document color is supported. /// - [DataMember(Name = "colorProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("colorProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentColorProvider { get; @@ -183,8 +181,8 @@ public SumType? DocumentColorProvider /// /// Gets or sets a value indicating whether document formatting is supported. /// - [DataMember(Name = "documentFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentFormattingProvider { get; @@ -194,8 +192,8 @@ public SumType? DocumentFormattingProvider /// /// Gets or sets a value indicating whether document range formatting is supported. /// - [DataMember(Name = "documentRangeFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentRangeFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentRangeFormattingProvider { get; @@ -205,8 +203,8 @@ public SumType? DocumentRangeFormattingPro /// /// Gets or sets the value which indicates if document on type formatting is supported. /// - [DataMember(Name = "documentOnTypeFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentOnTypeFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentOnTypeFormattingOptions? DocumentOnTypeFormattingProvider { get; @@ -216,8 +214,8 @@ public DocumentOnTypeFormattingOptions? DocumentOnTypeFormattingProvider /// /// Gets or sets a value indicating whether rename is supported. /// - [DataMember(Name = "renameProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("renameProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? RenameProvider { get; @@ -227,8 +225,8 @@ public SumType? RenameProvider /// /// Gets or sets the value which indicates if folding range is supported. /// - [DataMember(Name = "foldingRangeProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("foldingRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? FoldingRangeProvider { get; @@ -238,8 +236,8 @@ public SumType? FoldingRangeProvider /// /// Gets or sets the value which indicates if execute command is supported. /// - [DataMember(Name = "executeCommandProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("executeCommandProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ExecuteCommandOptions? ExecuteCommandProvider { get; @@ -249,8 +247,8 @@ public ExecuteCommandOptions? ExecuteCommandProvider /// /// Gets or sets a value indicating whether workspace symbols are supported. /// - [DataMember(Name = "workspaceSymbolProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspaceSymbolProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? WorkspaceSymbolProvider { get; @@ -260,8 +258,8 @@ public SumType? WorkspaceSymbolProvider /// /// Gets or sets experimental server capabilities. /// - [DataMember(Name = "experimental")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("experimental")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Experimental { get; @@ -271,8 +269,8 @@ public object? Experimental /// /// Gets or sets a value indicating whether the server supports linked editing range. /// - [DataMember(Name = "linkedEditingRangeProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("linkedEditingRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? LinkedEditingRangeProvider { get; @@ -282,8 +280,8 @@ public SumType? LinkedEditingRangeProvider /// /// Gets or sets the value which indicates if semantic tokens is supported. /// - [DataMember(Name = "semanticTokensProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokensProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensOptions? SemanticTokensOptions { get; @@ -293,8 +291,8 @@ public SemanticTokensOptions? SemanticTokensOptions /// /// Gets or sets the value which indicates what support the server has for pull diagnostics. /// - [DataMember(Name = "diagnosticProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnosticProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticOptions? DiagnosticOptions { get; @@ -304,8 +302,8 @@ public DiagnosticOptions? DiagnosticOptions /// /// Gets or sets the value which indicates what support the server has for inlay hints. /// - [DataMember(Name = "inlayHintProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHintProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? InlayHintOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs index d86b0e3ec53e7..b7da5703aedba 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/showMessage requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ShowMessageParams { /// /// Gets or sets the type of message. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public MessageType MessageType { get; @@ -27,7 +26,7 @@ public MessageType MessageType /// /// Gets or sets the message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs index 803ee910b8fa3..b8df89cb3f998 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/showMessageRequest requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ShowMessageRequestParams : ShowMessageParams { /// /// Gets or sets an array of s to present. /// - [DataMember(Name = "actions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("actions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MessageActionItem[]? Actions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs index ff7baa6c8c6c2..fbc02b0b71901 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature of something callable. This class is returned from the textDocument/signatureHelp request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelp { /// /// Gets or sets an array of signatures associated with the callable item. /// - [DataMember(Name = "signatures")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("signatures")] + [JsonRequired] public SignatureInformation[] Signatures { get; @@ -29,8 +27,8 @@ public SignatureInformation[] Signatures /// /// Gets or sets the active signature. If the value is omitted or falls outside the range of Signatures it defaults to zero. /// - [DataMember(Name = "activeSignature")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeSignature")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? ActiveSignature { get; @@ -40,8 +38,8 @@ public int? ActiveSignature /// /// Gets or sets the active parameter. If the value is omitted or falls outside the range of Signatures[ActiveSignature].Parameters it defaults to zero. /// - [DataMember(Name = "activeParameter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeParameter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? ActiveParameter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs index 4bc39650fc574..7d3a42675c951 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing additional information about the context in which a signature help request is triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpContext { /// /// Gets or sets the indicating how the signature help was triggered. /// - [DataMember(Name = "triggerKind")] + [JsonPropertyName("triggerKind")] public SignatureHelpTriggerKind TriggerKind { get; @@ -29,8 +27,8 @@ public SignatureHelpTriggerKind TriggerKind /// Gets or sets the character that caused signature help to be triggered. /// This value is null when triggerKind is not TriggerCharacter. /// - [DataMember(Name = "triggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TriggerCharacter { get; @@ -40,7 +38,7 @@ public string? TriggerCharacter /// /// Gets or sets a value indicating whether signature help was already showing when it was triggered. /// - [DataMember(Name = "isRetrigger")] + [JsonPropertyName("isRetrigger")] public bool IsRetrigger { get; @@ -50,8 +48,8 @@ public bool IsRetrigger /// /// Gets or sets the currently active . /// - [DataMember(Name = "activeSignatureHelp")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeSignatureHelp")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelp? ActiveSignatureHelp { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs index 9578bb1585af1..102ef229035f9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for signature help support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpOptions : IWorkDoneProgressOptions { /// /// Gets or sets the characters that trigger signature help automatically. /// - [DataMember(Name = "triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; @@ -30,8 +28,8 @@ public string[]? TriggerCharacters /// Gets or sets the characters that re-trigger signature help /// when signature help is already showing. /// - [DataMember(Name = "retriggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("retriggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? RetriggerCharacters { get; @@ -41,8 +39,8 @@ public string[]? RetriggerCharacters /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs index 765bf13dbb073..52fbd2f43e10a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the textDocument/signatureHelp request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpParams : TextDocumentPositionParams { /// /// Gets or sets the signature help context. /// - [DataMember(Name = "context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpContext? Context { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs index 7d12130ab2a96..447b91419344d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature help initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpSetting : DynamicRegistrationSetting { /// /// Gets or sets the information. /// - [DataMember(Name = "signatureInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureInformationSetting? SignatureInformation { get; @@ -30,8 +28,8 @@ public SignatureInformationSetting? SignatureInformation /// Gets or sets a value indicating whether additional context information /// is supported for the `textDocument/signatureHelp` request. /// - [DataMember(Name = "contextSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("contextSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ContextSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs index 2ffba29c58692..00ba1f6410d50 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways in which completion can be triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum SignatureHelpTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs index 6e68c4d386a97..462bd9c6e2b4a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a single signature of a callable item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureInformation { /// /// Gets or sets the label of this signature. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public string Label { get; @@ -28,8 +26,8 @@ public string Label /// /// Gets or sets the human-readable documentation of this signature. /// - [DataMember(Name = "documentation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Documentation { get; @@ -39,8 +37,8 @@ public SumType? Documentation /// /// Gets or sets the parameters of this signature. /// - [DataMember(Name = "parameters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ParameterInformation[]? Parameters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs index 083fa7dfc90a9..efb21c658a425 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature information initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureInformationSetting { /// /// Gets or sets the set of documentation formats the client supports. /// - [DataMember(Name = "documentationFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentationFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? DocumentationFormat { get; @@ -29,8 +27,8 @@ public MarkupKind[]? DocumentationFormat /// /// Gets or sets the parameter information the client supports. /// - [DataMember(Name = "parameterInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("parameterInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ParameterInformationSetting? ParameterInformation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SumType.cs b/src/Features/LanguageServer/Protocol/Protocol/SumType.cs index 3c50c415f59a8..6d656e070a348 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SumType.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SumType.cs @@ -7,9 +7,7 @@ namespace Roslyn.LanguageServer.Protocol using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - using Newtonsoft.Json; - using System.Runtime.CompilerServices; - using Microsoft.CommonLanguageServerProtocol.Framework; + using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer; /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs index dc3b38d0cdacb..c35871bdaef2b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing information about programming constructs like variables, classes, interfaces, etc. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolInformation : IEquatable { /// /// Gets or sets the name of this symbol. /// - [DataMember(Name = "name")] + [JsonPropertyName("name")] public string Name { get; @@ -30,7 +28,7 @@ public string Name /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public SymbolKind Kind { get; @@ -40,7 +38,7 @@ public SymbolKind Kind /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "location")] + [JsonPropertyName("location")] public Location Location { get; @@ -50,8 +48,8 @@ public Location Location /// /// Gets or sets the name of the symbol containing this symbol. /// - [DataMember(Name = "containerName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("containerName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainerName { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs index e2d98be0daac4..77c9d57b8b8a3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; - /// /// Enum which represents the various kinds of symbols. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "Names are defined by the LSP")] internal enum SymbolKind { diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs index ce32110d895d1..3b9f98a15aad2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the symbol kind setting in initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolKindSetting { /// /// Gets or sets the types of symbol kind the client supports. /// - [DataMember(Name = "valueSet")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("valueSet")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKind[]? ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs index a1b609d5a041e..bad49978458a1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the symbol setting for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolSetting : DynamicRegistrationSetting { /// /// Gets or sets the information. /// - [DataMember(Name = "symbolKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbolKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKindSetting? SymbolKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs index 87da1fdc6ac1e..82e339cb5ea56 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents synchronization initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SynchronizationSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether WillSave event is supported. /// - [DataMember(Name = "willSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSave { get; @@ -29,8 +27,8 @@ public bool WillSave /// /// Gets or sets a value indicating whether WillSaveWaitUntil event is supported. /// - [DataMember(Name = "willSaveWaitUntil")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSaveWaitUntil")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSaveWaitUntil { get; @@ -40,8 +38,8 @@ public bool WillSaveWaitUntil /// /// Gets or sets a value indicating whether DidSave event is supported. /// - [DataMember(Name = "didSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("didSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DidSave { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs b/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs index e5dfaffa0e439..2f27a28e442e8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TagSupport { /// /// Gets or sets a value indicating the tags supported by the client. /// - [DataMember(Name = "valueSet")] + [JsonPropertyName("valueSet")] public DiagnosticTag[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs new file mode 100644 index 0000000000000..fa152aa3d8140 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.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.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol +{ + /// + /// Class representing the registration options for didChange events. + /// + /// See the Language Server Protocol specification for additional information. + /// + internal class TextDocumentChangeRegistrationOptions : TextDocumentRegistrationOptions + { + /// + /// How documents are synced to the server. See + /// and . + /// + [JsonPropertyName("syncKind")] + public TextDocumentSyncKind SyncKind + { + get; + set; + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs index 5ea26816eaf94..ee1b4051ae3ea 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents text document capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentClientCapabilities { /// /// Gets or sets the synchronization setting. /// - [DataMember(Name = "synchronization")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("synchronization")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SynchronizationSetting? Synchronization { get; @@ -29,8 +27,8 @@ public SynchronizationSetting? Synchronization /// /// Gets or sets the completion setting. /// - [DataMember(Name = "completion")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionSetting? Completion { get; @@ -40,8 +38,8 @@ public CompletionSetting? Completion /// /// Gets or sets the setting which determines if hover can be dynamically registered. /// - [DataMember(Name = "hover")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("hover")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public HoverSetting? Hover { get; @@ -51,8 +49,8 @@ public HoverSetting? Hover /// /// Gets or sets the setting which determines if signature help can be dynamically registered. /// - [DataMember(Name = "signatureHelp")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureHelp")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpSetting? SignatureHelp { get; @@ -62,8 +60,8 @@ public SignatureHelpSetting? SignatureHelp /// /// Gets or sets the setting which determines if definition can be dynamically registered. /// - [DataMember(Name = "definition")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("definition")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Definition { get; @@ -73,8 +71,8 @@ public DynamicRegistrationSetting? Definition /// /// Gets or sets the settings which determines if type definition can be dynamically registered. /// - [DataMember(Name = "typeDefinition")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("typeDefinition")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? TypeDefinition { get; @@ -84,8 +82,8 @@ public DynamicRegistrationSetting? TypeDefinition /// /// Gets or sets the settings which determines if implementation can be dynamically registered. /// - [DataMember(Name = "implementation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("implementation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Implementation { get; @@ -95,8 +93,8 @@ public DynamicRegistrationSetting? Implementation /// /// Gets or sets the setting which determines if references can be dynamically registered. /// - [DataMember(Name = "references")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("references")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? References { get; @@ -106,8 +104,8 @@ public DynamicRegistrationSetting? References /// /// Gets or sets the setting which determines if document highlight can be dynamically registered. /// - [DataMember(Name = "documentHighlight")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentHighlight")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DocumentHighlight { get; @@ -117,8 +115,8 @@ public DynamicRegistrationSetting? DocumentHighlight /// /// Gets or sets the setting which determines if document symbol can be dynamically registered. /// - [DataMember(Name = "documentSymbol")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentSymbol")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentSymbolSetting? DocumentSymbol { get; @@ -128,8 +126,8 @@ public DocumentSymbolSetting? DocumentSymbol /// /// Gets or sets the setting which determines if code action can be dynamically registered. /// - [DataMember(Name = "codeAction")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeAction")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionSetting? CodeAction { get; @@ -139,8 +137,8 @@ public CodeActionSetting? CodeAction /// /// Gets or sets the setting which determines if code lens can be dynamically registered. /// - [DataMember(Name = "codeLens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? CodeLens { get; @@ -150,8 +148,8 @@ public DynamicRegistrationSetting? CodeLens /// /// Gets or sets the setting which determines if document link can be dynamically registered. /// - [DataMember(Name = "documentLink")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentLink")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DocumentLink { get; @@ -161,8 +159,8 @@ public DynamicRegistrationSetting? DocumentLink /// /// Gets or sets the setting which determines if formatting can be dynamically registered. /// - [DataMember(Name = "formatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("formatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Formatting { get; @@ -172,8 +170,8 @@ public DynamicRegistrationSetting? Formatting /// /// Gets or sets the setting which determines if range formatting can be dynamically registered. /// - [DataMember(Name = "rangeFormatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeFormatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? RangeFormatting { get; @@ -183,8 +181,8 @@ public DynamicRegistrationSetting? RangeFormatting /// /// Gets or sets the setting which determines if on type formatting can be dynamically registered. /// - [DataMember(Name = "onTypeFormatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("onTypeFormatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? OnTypeFormatting { get; @@ -194,8 +192,8 @@ public DynamicRegistrationSetting? OnTypeFormatting /// /// Gets or sets the setting which determines if rename can be dynamically registered. /// - [DataMember(Name = "rename")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rename")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RenameClientCapabilities? Rename { get; @@ -205,8 +203,8 @@ public RenameClientCapabilities? Rename /// /// Gets or sets the setting publish diagnostics setting. /// - [DataMember(Name = "publishDiagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("publishDiagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PublishDiagnosticsSetting? PublishDiagnostics { get; @@ -216,7 +214,7 @@ public PublishDiagnosticsSetting? PublishDiagnostics /// /// Gets or sets the setting which determines how folding range is supported. /// - [DataMember(Name = "foldingRange")] + [JsonPropertyName("foldingRange")] public FoldingRangeSetting? FoldingRange { get; @@ -226,8 +224,8 @@ public FoldingRangeSetting? FoldingRange /// /// Gets or sets the setting which determines if linked editing range can be dynamically registered. /// - [DataMember(Name = "linkedEditingRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("linkedEditingRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting LinkedEditingRange { get; @@ -237,8 +235,8 @@ public DynamicRegistrationSetting LinkedEditingRange /// /// Gets or sets a setting indicating whether semantic tokens is supported. /// - [DataMember(Name = "semanticTokens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensSetting? SemanticTokens { get; @@ -248,8 +246,8 @@ public SemanticTokensSetting? SemanticTokens /// /// Gets or sets the setting which determines what support the client has for pull diagnostics. /// - [DataMember(Name = "diagnostic")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostic")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticSetting? Diagnostic { get; @@ -259,8 +257,8 @@ public DiagnosticSetting? Diagnostic /// /// Gets or sets the setting which determines what support the client has for pull diagnostics. /// - [DataMember(Name = "inlayHint")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHint")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintSetting? InlayHint { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs index 8c2dd3dbb9566..64568623fe4dc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which encapsulates a text document changed event. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentContentChangeEvent { /// /// Gets or sets the range of the text that was changed. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -28,8 +26,8 @@ public Range Range /// /// Gets or sets the length of the range that got replaced. /// - [DataMember(Name = "rangeLength")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeLength")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? RangeLength { get; @@ -39,7 +37,7 @@ public int? RangeLength /// /// Gets or sets the new text of the range/document. /// - [DataMember(Name = "text")] + [JsonPropertyName("text")] public string Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs index bfe8999beff4c..26af2f8e87483 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing a set of changes to a single text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentEdit { /// /// Gets or sets a document identifier indication which document to apply the edits to. /// - [DataMember(Name = "textDocument", IsRequired = true)] + [JsonPropertyName("textDocument")] + [JsonRequired] public OptionalVersionedTextDocumentIdentifier TextDocument { get; @@ -27,7 +27,8 @@ public OptionalVersionedTextDocumentIdentifier TextDocument /// /// Gets or sets the array of edits to be applied to the document. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public TextEdit[] Edits { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs index 78ffa87d73c42..9ec8f5a0c8804 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which identifies a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentIdentifier : IEquatable { /// /// Gets or sets the URI of the text document. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs index 4885efdb848b9..9a627e3ff7622 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentItem { /// /// Gets or sets the document URI. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the document language identifier. /// - [DataMember(Name = "languageId")] + [JsonPropertyName("languageId")] public string LanguageId { get; @@ -40,7 +38,7 @@ public string LanguageId /// /// Gets or sets the document version. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int Version { get; @@ -50,7 +48,7 @@ public int Version /// /// Gets or sets the content of the opened text document. /// - [DataMember(Name = "text")] + [JsonPropertyName("text")] public string Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs index 7886b68fb690a..ab430a5ed29c6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a position within a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentPositionParams : ITextDocumentPositionParams { /// /// Gets or sets the value which identifies the document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the value which indicates the position within the document. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs index 6fee4bee03e0f..94a0182345207 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the registration options for many different text document functions. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentRegistrationOptions : ITextDocumentRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs index c0ad11d2cf815..3fab506457c1f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; - /// /// Enum representing the reason a document was saved. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum TextDocumentSaveReason { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs index 105d0dbe2d5fe..5097bb3586c9c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways to sync text documents. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum TextDocumentSyncKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs index 2c398b13b22e2..72f5e2a1c179e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents configuration values indicating how text documents should be synced. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentSyncOptions { /// /// Gets or sets a value indicating whether open and close notifications are sent to the server. /// - [DataMember(Name = "openClose")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("openClose")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool OpenClose { get; @@ -30,8 +28,8 @@ public bool OpenClose /// /// Gets or sets the value indicating how text documents are synced with the server. /// - [DataMember(Name = "change")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("change")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [DefaultValue(TextDocumentSyncKind.None)] public TextDocumentSyncKind? Change { @@ -42,8 +40,8 @@ public TextDocumentSyncKind? Change /// /// Gets or sets a value indicating whether 'will save' notifications are sent to the server. /// - [DataMember(Name = "willSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSave { get; @@ -53,8 +51,8 @@ public bool WillSave /// /// Gets or sets a value indicating whether 'will save until' notifications are sent to the server. /// - [DataMember(Name = "willSaveWaitUntil")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSaveWaitUntil")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSaveWaitUntil { get; @@ -64,8 +62,8 @@ public bool WillSaveWaitUntil /// /// Gets or sets a value indicating whether save notifications are sent to the server. /// - [DataMember(Name = "save")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("save")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Save { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs index dccc1ce6887c5..c25885343f6c4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a text edit to a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextEdit { /// /// Gets or sets the value which indicates the range of the text edit. /// - [DataMember(Name = "range", IsRequired = true)] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -27,7 +27,7 @@ public Range Range /// /// Gets or sets the value of the new text. /// - [DataMember(Name = "newText")] + [JsonPropertyName("newText")] public string NewText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs index 92261d6fc1790..cf1ec787ae2f4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs @@ -5,7 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the language server trace setting. diff --git a/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs index 031e241a9fa5e..b17c3bec86600 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TypeDefinitionOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs index eaff6e3e52e55..238da08d40af5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing a diagnostic report indicating that the last returned report is still accurate. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class UnchangedDocumentDiagnosticReport { /// /// Gets the kind of this report. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] #pragma warning disable CA1822 // Mark members as static public string Kind => DocumentDiagnosticReportKind.Unchanged; #pragma warning restore CA1822 // Mark members as static @@ -26,7 +25,7 @@ internal class UnchangedDocumentDiagnosticReport /// /// Gets or sets the optional result id. /// - [DataMember(Name = "resultId")] + [JsonPropertyName("resultId")] public string ResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs b/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs index 03493010e0a44..ea2c9e8efbea3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the information needed for unregistering a capability. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Unregistration { /// /// Gets or sets the id of the unregistration. /// - [DataMember(Name = "id")] + [JsonPropertyName("id")] public string Id { get; @@ -27,7 +26,7 @@ public string Id /// /// Gets or sets the method to unregister. /// - [DataMember(Name = "method")] + [JsonPropertyName("method")] public string Method { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs index 54d954451b88e..118084c82212d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameter sent for the client/unregisterCapability request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class UnregistrationParams { /// /// Gets or sets the capabilities to unregister. /// - [DataMember(Name = "unregistrations")] + [JsonPropertyName("unregistrations")] public Unregistration[] Unregistrations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs index ecffd30fcdbba..296808d6a31f4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Globalization; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document, but has a version identifier. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class VersionedTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the version of the document. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs index fb88efda26c86..46269ba196183 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the textDocument/willSave request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WillSaveTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the representing the document to be saved. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the reason that the text document was saved. /// - [DataMember(Name = "reason")] + [JsonPropertyName("reason")] public TextDocumentSaveReason Reason { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs index 74cb04f4e3912..a045161ec3973 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceClientCapabilities { /// /// Gets or sets a value indicating whether apply edit is supported. /// - [DataMember(Name = "applyEdit")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("applyEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ApplyEdit { get; @@ -29,8 +27,8 @@ public bool ApplyEdit /// /// Gets or sets the workspace edit setting. /// - [DataMember(Name = "workspaceEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspaceEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEditSetting? WorkspaceEdit { get; @@ -40,8 +38,8 @@ public WorkspaceEditSetting? WorkspaceEdit /// /// Gets or sets the setting which determines if did change configuration can be dynamically registered. /// - [DataMember(Name = "didChangeConfiguration")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("didChangeConfiguration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DidChangeConfiguration { get; @@ -51,8 +49,8 @@ public DynamicRegistrationSetting? DidChangeConfiguration /// /// Gets or sets the setting which determines if did change watched files can be dynamically registered. /// - [DataMember(Name = "didChangeWatchedFiles")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("didChangeWatchedFiles")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DidChangeWatchedFiles { get; @@ -62,8 +60,8 @@ public DynamicRegistrationSetting? DidChangeWatchedFiles /// /// Gets or sets the setting which determines if symbols can be dynamically registered. /// - [DataMember(Name = "symbol")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbol")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolSetting? Symbol { get; @@ -73,8 +71,8 @@ public SymbolSetting? Symbol /// /// Gets or sets the setting which determines if execute command can be dynamically registered. /// - [DataMember(Name = "executeCommand")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("executeCommand")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? ExecuteCommand { get; @@ -84,8 +82,8 @@ public DynamicRegistrationSetting? ExecuteCommand /// /// Gets or sets capabilities specific to the semantic token requests scoped to the workspace. /// - [DataMember(Name = "semanticTokens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensWorkspaceSetting? SemanticTokens { get; @@ -95,8 +93,8 @@ public SemanticTokensWorkspaceSetting? SemanticTokens /// /// Gets or sets capabilities indicating what support the client has for workspace pull diagnostics. /// - [DataMember(Name = "diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticWorkspaceSetting? Diagnostics { get; @@ -106,8 +104,8 @@ public DiagnosticWorkspaceSetting? Diagnostics /// /// Gets or sets the capabilities if client support 'workspace/configuration' requests. /// - [DataMember(Name = "configuration")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("configuration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Configuration { get; @@ -117,8 +115,8 @@ public bool Configuration /// /// Gets of sets capabilities specific to the inlay hint requests scoped to the workspace. /// - [DataMember(Name = "inlayHint")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHint")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintWorkspaceSetting? InlayHint { get; @@ -128,8 +126,8 @@ public InlayHintWorkspaceSetting? InlayHint /// /// Gets of sets capabilities specific to the code lens requests scoped to the workspace. /// - [DataMember(Name = "codeLens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeLensWorkspaceSetting? CodeLens { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs index f8c2cdb7dcfef..850f8c4fa31ab 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the workspace diagnostic request parameters @@ -17,14 +16,13 @@ namespace Roslyn.LanguageServer.Protocol; /// Note that the first literal send needs to be a /// followed by n literals. /// -[DataContract] internal class WorkspaceDiagnosticParams : IPartialResultParams> { /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress>? PartialResultToken { get; @@ -34,8 +32,8 @@ public IProgress /// Gets or sets the identifier for which the client is requesting diagnostics for. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -45,7 +43,7 @@ public string? Identifier /// /// Gets or sets the result id of a previous diagnostics response if provided. /// - [DataMember(Name = "previousResultIds")] + [JsonPropertyName("previousResultIds")] public PreviousResultId[] PreviousResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs index fff52c02f2e2e..49b33e63f135a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs @@ -3,23 +3,19 @@ // See the LICENSE file in the project root for more information. namespace Roslyn.LanguageServer.Protocol; - -using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a workspace diagnostic report. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class WorkspaceDiagnosticReport { /// /// Gets or sets the items in this diagnostic report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public SumType[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs index f2e3387cc2c88..1c29c40074e81 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs @@ -3,23 +3,19 @@ // See the LICENSE file in the project root for more information. namespace Roslyn.LanguageServer.Protocol; - -using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a partial result for a workspace diagnostic report. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class WorkspaceDiagnosticReportPartialResult { /// /// Gets or sets the items in this diagnostic report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public SumType[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs index 23fba470b1e07..d3739a5a90425 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a request sent from a language server to modify resources in the workspace. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceEdit { /// /// Gets or sets a dictionary holding changes to existing resources. /// - [DataMember(Name = "changes")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("changes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary? Changes { get; @@ -30,8 +28,8 @@ public Dictionary? Changes /// /// Gets or sets an array representing versioned document changes. /// - [DataMember(Name = "documentChanges")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentChanges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType[]>? DocumentChanges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs index 26265677883bd..c3aee573bdf8f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization settings for workspace edit. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceEditSetting { /// /// Gets or sets a value indicating whether document changes event is supported. /// - [DataMember(Name = "documentChanges")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("documentChanges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DocumentChanges { get; @@ -29,8 +27,8 @@ public bool DocumentChanges /// /// GEts or sets the resource operations the client supports. /// - [DataMember(Name = "resourceOperations")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resourceOperations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ResourceOperationKind[]? ResourceOperations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs index 7a2f9d84ee9d2..95ec1cad08b96 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a full document diagnostic report for workspace diagnostic result. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class WorkspaceFullDocumentDiagnosticReport : FullDocumentDiagnosticReport { /// /// Gets or sets the URI associated with this diagnostic report. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -32,7 +30,7 @@ public Uri Uri /// Gets or sets the version number for which the diagnostics are reported. /// If the document is not marked as open 'null' can be provided. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs index 8f2f697842d37..01ac93b66f538 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceSymbolOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs index 2ef10ea9fa3ef..bc70cf64c559a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that's sent with the 'workspace/symbol' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceSymbolParams : IPartialResultParams { /// /// Gets or sets the query (a non-empty string). /// - [DataMember(Name = "query")] + [JsonPropertyName("query")] public string Query { get; @@ -29,8 +27,8 @@ public string Query /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs index d7aa7192da725..1fc1bd8cda697 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a unchanged document diagnostic report for workspace diagnostic result. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class WorkspaceUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport { /// /// Gets or sets the URI associated with this diagnostic report. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -32,7 +30,7 @@ public Uri Uri /// Gets or sets the version number for which the diagnostics are reported. /// If the document is not marked as open 'null' can be provided. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs index 8570c4d0db31b..670ced6aa0ad2 100644 --- a/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs +++ b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs @@ -5,21 +5,20 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer { - internal sealed class RoslynLanguageServer : AbstractLanguageServer, IOnInitialized + internal sealed class RoslynLanguageServer : SystemTextJsonLanguageServer, IOnInitialized { private readonly AbstractLspServiceProvider _lspServiceProvider; private readonly ImmutableDictionary>> _baseServices; @@ -28,19 +27,17 @@ internal sealed class RoslynLanguageServer : AbstractLanguageServer supportedLanguages, WellKnownLspServerKinds serverKind) - : base(jsonRpc, serializer, logger) + : base(jsonRpc, serializerOptions, logger) { _lspServiceProvider = lspServiceProvider; _serverKind = serverKind; - VSCodeInternalExtensionUtilities.AddVSCodeInternalExtensionConverters(serializer); - // Create services that require base dependencies (jsonrpc) or are more complex to create to the set manually. _baseServices = GetBaseServices(jsonRpc, logger, capabilitiesProvider, hostServices, serverKind, supportedLanguages); @@ -48,6 +45,13 @@ public RoslynLanguageServer( Initialize(); } + public static SystemTextJsonFormatter CreateJsonMessageFormatter() + { + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddLspSerializerOptions(); + return messageFormatter; + } + protected override ILspServices ConstructLspServices() { return _lspServiceProvider.CreateServices(_serverKind, _baseServices); @@ -113,7 +117,7 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon return Task.CompletedTask; } - protected override string GetLanguageForRequest(string methodName, JToken? parameters) + protected override string GetLanguageForRequest(string methodName, JsonElement? parameters) { if (parameters == null) { @@ -134,12 +138,11 @@ protected override string GetLanguageForRequest(string methodName, JToken? param // { "textDocument": { "uri": "" ... } ... } // // We can easily identify the URI for the request by looking for this structure - var textDocumentToken = parameters["textDocument"] ?? parameters["_vs_textDocument"]; - if (textDocumentToken is not null) + if (parameters.Value.TryGetProperty("textDocument", out var textDocumentToken) || + parameters.Value.TryGetProperty("_vs_textDocument", out textDocumentToken)) { - var uriToken = textDocumentToken["uri"]; - Contract.ThrowIfNull(uriToken, "textDocument does not have a uri property"); - var uri = uriToken.ToObject(_jsonSerializer); + var uriToken = textDocumentToken.GetProperty("uri"); + var uri = JsonSerializer.Deserialize(uriToken, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(uri, "Failed to deserialize uri property"); var language = lspWorkspaceManager.GetLanguageForUri(uri); Logger.LogInformation($"Using {language} from request text document"); @@ -150,10 +153,10 @@ protected override string GetLanguageForRequest(string methodName, JToken? param // { "data": { "TextDocument": { "uri": "" ... } ... } ... } // // We can deserialize the data object using our unified DocumentResolveData. - var dataToken = parameters["data"]; - if (dataToken is not null) + //var dataToken = parameters["data"]; + if (parameters.Value.TryGetProperty("data", out var dataToken)) { - var data = dataToken.ToObject(_jsonSerializer); + var data = JsonSerializer.Deserialize(dataToken, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(data, "Failed to document resolve data object"); var language = lspWorkspaceManager.GetLanguageForUri(data.TextDocument.Uri); Logger.LogInformation($"Using {language} from data text document"); @@ -165,7 +168,8 @@ protected override string GetLanguageForRequest(string methodName, JToken? param return LanguageServerConstants.DefaultLanguageName; static bool ShouldUseDefaultLanguage(string methodName) - => methodName switch + { + return methodName switch { Methods.InitializeName => true, Methods.InitializedName => true, @@ -177,6 +181,7 @@ static bool ShouldUseDefaultLanguage(string methodName) Methods.ExitName => true, _ => false, }; + } } } } diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 92ac29ae53f61..bb5490d3220ea 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -3,15 +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.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 289f609e7df67..5a7b6a6a69a09 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 13f1f5a4f7fb6..e8c576772aa0e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -12,11 +12,11 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions; @@ -93,7 +93,7 @@ void M() var topLevelAction = Assert.Single(results.Where(action => action.Title == titlePath[0])); var introduceConstant = topLevelAction.Children.FirstOrDefault( - r => ((JObject)r.Data!).ToObject()!.UniqueIdentifier == titlePath[1]); + r => JsonSerializer.Deserialize((JsonElement)r.Data!, ProtocolConversions.LspJsonSerializerOptions)!.UniqueIdentifier == titlePath[1]); AssertJsonEquals(expected, introduceConstant); } @@ -323,8 +323,7 @@ private static async Task RunGetCodeActionResolveAsync( private static CodeActionResolveData? GetCodeActionResolveData(CodeAction codeAction) { - return ((JToken)codeAction.Data!).ToObject(); - + return JsonSerializer.Deserialize((JsonElement)codeAction.Data!, ProtocolConversions.LspJsonSerializerOptions); } internal static CodeActionParams CreateCodeActionParams(LSP.Location caret) @@ -349,7 +348,7 @@ internal static VSInternalCodeAction CreateCodeAction( Title = title, Kind = kind, Children = children, - Data = JToken.FromObject(data), + Data = JsonSerializer.SerializeToElement(data, ProtocolConversions.LspJsonSerializerOptions), Diagnostics = diagnostics, Edit = edit, Group = groupName, diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index 7812d7ac25c1a..dea32cb40c3e7 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -69,7 +69,7 @@ private static async Task ExecuteRunCodeActionCommandAsync( Command = CodeActionsHandler.RunCodeActionCommandName, Arguments = [ - JToken.FromObject(codeActionData) + JsonSerializer.SerializeToElement(codeActionData, ProtocolConversions.LspJsonSerializerOptions) ] }; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs index f3db678c7c4db..0472b4349df1b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs @@ -3,10 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; -using Newtonsoft.Json; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -387,7 +387,7 @@ void UseM() var actualCodeLenses = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParamsDoc1, CancellationToken.None); var firstCodeLens = actualCodeLenses.First(); - var data = JsonConvert.DeserializeObject(firstCodeLens.Data!.ToString()); + var data = JsonSerializer.Deserialize(firstCodeLens.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); // Update the document so the syntax version changes diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs index cfa16156b82ce..19994181f2183 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs @@ -297,7 +297,7 @@ class A { }"; AssertJsonEquals(completionParams.TextDocument, resolvedItem.Command.Arguments[0]); AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)14, resolvedItem.Command.Arguments[3]); + Assert.Equal(14, resolvedItem.Command.Arguments[3]); } [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1755955")] @@ -933,7 +933,7 @@ public async Task TestHandleExceptionFromGetCompletionChange(bool mutatingLspWor AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)-1, resolvedItem.Command.Arguments[3]); + Assert.Equal(-1, resolvedItem.Command.Arguments[3]); } } } @@ -990,7 +990,7 @@ public class MyClass : BaseClass AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)268, resolvedItem.Command.Arguments[3]); + Assert.Equal(268, resolvedItem.Command.Arguments[3]); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/6495")] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs index 2ff2d3846bb51..d03e70a4b2c26 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text.Adornments; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -245,7 +244,6 @@ void M() var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); - Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"```csharp void A.AMethod(int i) @@ -307,7 +305,6 @@ void M() var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); - Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"void A.AMethod(int i) A cref A.AMethod(int) @@ -455,8 +452,7 @@ private static async Task GetCompletionItemToResolveAsync( Assert.NotNull(vsCompletionList.Data); } - var serverCompletionItem = completionList.Items.FirstOrDefault(item => item.Label == label); - var clientCompletionItem = ConvertToClientCompletionItem((T)serverCompletionItem); + var clientCompletionItem = (T)completionList.Items.FirstOrDefault(item => item.Label == label); return clientCompletionItem; } @@ -482,13 +478,6 @@ completionList is VSInternalCompletionList vsCompletionList && return completionList; } - private static T ConvertToClientCompletionItem(T serverCompletionItem) where T : LSP.CompletionItem - { - var serializedItem = JsonConvert.SerializeObject(serverCompletionItem); - var clientCompletionItem = JsonConvert.DeserializeObject(serializedItem); - return clientCompletionItem; - } - private class TestCaretOutOfScopeCompletionService : CompletionService { public TestCaretOutOfScopeCompletionService(SolutionServices services) : base(services, AsynchronousOperationListenerProvider.NullProvider) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs b/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs index 728291fc02f5c..a70495d329fc3 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs @@ -5,13 +5,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.Configuration; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -199,7 +199,7 @@ public void ClientRegisterCapability(RegistrationParams @registrationParams, Can } [JsonRpcMethod(Methods.WorkspaceConfigurationName, UseSingleObjectParameterDeserialization = true)] - public JArray WorkspaceConfigurationName(ConfigurationParams configurationParams, CancellationToken _) + public List WorkspaceConfigurationName(ConfigurationParams configurationParams, CancellationToken _) { ReceivedWorkspaceConfigurationRequest = true; var expectConfigurationItemsNumber = DidChangeConfigurationNotificationHandler.SupportedOptions.Sum(option => option is IPerLanguageValuedOption ? 2 : 1); @@ -211,7 +211,7 @@ public JArray WorkspaceConfigurationName(ConfigurationParams configurationParams AssertSectionPattern(item.Section); } - return JArray.FromObject(MockClientSideValues); + return MockClientSideValues; } public void SetClientSideOptionValues(bool setToDefaultValue) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs index cb85dd18a0c98..9ee002a27e92e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Public; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using StreamJsonRpc; @@ -53,7 +53,7 @@ public async Task TestPublicDiagnosticSourcesAreRegisteredWhenSupported(bool mut // Get all registrations for diagnostics (note that workspace registrations are registered against document method name). var diagnosticRegistrations = registrations .Where(r => r.Method == Methods.TextDocumentDiagnosticName) - .Select(r => ((JObject)r.RegisterOptions!).ToObject()!); + .Select(r => JsonSerializer.Deserialize((JsonElement)r.RegisterOptions!, ProtocolConversions.LspJsonSerializerOptions)!); Assert.NotEmpty(diagnosticRegistrations); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 1141ce6e1a12d..d575909fc21b9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -180,6 +180,28 @@ static void Main(string[] args) Assert.Equal(vsDiagnostic.ExpandedMessage, AnalyzersResources.Avoid_unused_parameters_in_your_code_If_the_parameter_cannot_be_removed_then_change_its_name_so_it_starts_with_an_underscore_and_is_optionally_followed_by_an_integer_such_as__comma__1_comma__2_etc_These_are_treated_as_special_discard_symbol_names); } + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2050705")] + public async Task TestDocumentDiagnosticsUsesNullForExpandedMessage(bool mutatingLspWorkspace) + { + var markup = +@"class A {"; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); + + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + + await OpenDocumentAsync(testLspServer, document); + + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI(), useVSDiagnostics: true); + + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Null(vsDiagnostic.ExpandedMessage); + } + [Theory, CombinatorialData] public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_Category(bool mutatingLspWorkspace) { diff --git a/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs index adb5bc1e36834..4eeaea3b54dcb 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs @@ -4,15 +4,13 @@ using System; using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -125,14 +123,11 @@ public async Task ThrowsIfDeserializationFails(bool mutatingLspWorkspace) await Assert.ThrowsAsync(async () => await server.ExecuteRequestAsync(TestDocumentHandler.MethodName, request, CancellationToken.None)); } - [DataContract] - internal record TestRequestTypeOne([property: DataMember(Name = "textDocument"), JsonProperty(Required = Required.Always)] TextDocumentIdentifier TextDocumentIdentifier); + internal record TestRequestTypeOne([property: JsonPropertyName("textDocument"), JsonRequired] TextDocumentIdentifier TextDocumentIdentifier); - [DataContract] - internal record TestRequestTypeTwo([property: DataMember(Name = "textDocument"), JsonProperty(Required = Required.Always)] TextDocumentIdentifier TextDocumentIdentifier); + internal record TestRequestTypeTwo([property: JsonPropertyName("textDocument"), JsonRequired] TextDocumentIdentifier TextDocumentIdentifier); - [DataContract] - internal record TestRequestTypeThree([property: DataMember(Name = "someValue")] string SomeValue); + internal record TestRequestTypeThree([property: JsonPropertyName("someValue")] string SomeValue); [ExportCSharpVisualBasicStatelessLspService(typeof(TestDocumentHandler)), PartNotDiscoverable, Shared] [LanguageServerEndpoint(MethodName, LanguageServerConstants.DefaultLanguageName)] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index d4d0801f11fcc..f6392ba5acec8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -12,12 +12,12 @@ using Roslyn.LanguageServer.Protocol; using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Text; -using Newtonsoft.Json; using Roslyn.Test.Utilities; using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.InlayHint { @@ -134,7 +134,7 @@ void M() var actualInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); var firstInlayHint = actualInlayHints.First(); - var data = JsonConvert.DeserializeObject(firstInlayHint.Data!.ToString()); + var data = JsonSerializer.Deserialize(firstInlayHint.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); var firstResultId = data.ResultId; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 7607c7dc55667..7bc5c9b1c2c85 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -5,13 +5,15 @@ #nullable disable using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Roslyn.Text.Adornments; using Roslyn.Utilities; @@ -91,7 +93,7 @@ void M2() // with the test creating one, and the handler another, we have to unwrap. // Additionally, the VS LSP protocol specifies T from IProgress as an object and not as the actual VSInternalReferenceItem // so we have to correctly convert the JObject into the expected type. - results = progress.GetValues().Select(reference => ((JArray)reference).ToObject()).SelectMany(v => v).ToArray(); + results = progress.GetValues().SelectMany(r => (List)r).Select(r => JsonSerializer.Deserialize((JsonElement)r, ProtocolConversions.LspJsonSerializerOptions)).ToArray(); Assert.NotNull(results); Assert.NotEmpty(results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs index 73f22e2e68fc3..40e6fa2e14677 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs @@ -111,7 +111,7 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var servicesProvider = workspace.ExportProvider.GetExportedValue(); - var messageFormatter = CreateJsonMessageFormatter(); + var messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter)) { ExceptionStrategy = ExceptionProcessing.ISerializable, @@ -120,7 +120,7 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var logger = NoOpLspLogger.Instance; var languageServer = new RoslynLanguageServer( - servicesProvider, jsonRpc, messageFormatter.JsonSerializer, + servicesProvider, jsonRpc, messageFormatter.JsonSerializerOptions, capabilitiesProvider, logger, workspace.Services.HostServices, diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj index 20ba57b5f5573..6aefd270a5757 100644 --- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj +++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj @@ -81,19 +81,9 @@ - - - - - - - - - diff --git a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb index f736e974e5c10..ce1bdcd6704e8 100644 --- a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb +++ b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb @@ -3,10 +3,10 @@ ' See the LICENSE file in the project root for more information. Imports System.Linq +Imports System.Text.Json Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Graph Imports Microsoft.CodeAnalysis.Test.Utilities -Imports Newtonsoft.Json Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests @@ -44,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests Dim document = semanticTokensWorkspace.CurrentSolution.Projects.Single().Documents.Single() Dim tokens = lsif.GetSemanticTokens(document) - Dim serializedTokens = JsonConvert.SerializeObject(tokens) + Dim serializedTokens = JsonSerializer.Serialize(tokens) Assert.Equal(expectedTokens, serializedTokens) End Using diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index 94694f171c585..e4144cbdb1143 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -7,6 +7,7 @@ Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ChangeSignature +Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.FindSymbols Imports Microsoft.CodeAnalysis.Formatting @@ -734,8 +735,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Return results.ToImmutableAndFree() End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) - Return SpecializedCollections.SingletonEnumerable(Of AbstractFormattingRule)(New ChangeSignatureFormattingRule()).Concat(Formatter.GetDefaultFormattingRules(document)) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) + Dim coreRules = Formatter.GetDefaultFormattingRules(document) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(1 + coreRules.Length) + result.Add(New ChangeSignatureFormattingRule()) + result.AddRange(coreRules) + Return result.MoveToImmutable() End Function Protected Overrides Function TransferLeadingWhitespaceTrivia(Of T As SyntaxNode)(newArgument As T, oldArgument As SyntaxNode) As T diff --git a/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb b/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb index d34283af4824e..2c494217af6cb 100644 --- a/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb +++ b/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb @@ -130,11 +130,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EncapsulateField Return NameGenerator.GenerateUniqueName(propertyName, containingTypeMemberNames.ToSet(), StringComparer.OrdinalIgnoreCase) End Function - Friend Overrides Function GetConstructorNodes(containingType As INamedTypeSymbol) As IEnumerable(Of SyntaxNode) + Protected Overrides Function GetConstructorNodes(containingType As INamedTypeSymbol) As IEnumerable(Of SyntaxNode) Return containingType.Constructors.SelectMany(Function(c As IMethodSymbol) Return c.DeclaringSyntaxReferences.Select(Function(d) d.GetSyntax().Parent) End Function) End Function - End Class End Namespace diff --git a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb index 6af12e705418b..277e3494c07c8 100644 --- a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb +++ b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition +Imports System.Threading Imports Microsoft.CodeAnalysis.GoToDefinition Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Operations @@ -12,7 +13,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.GoToDefinition - Friend Class VisualBasicGoToDefinitionSymbolService + Friend NotInheritable Class VisualBasicGoToDefinitionSymbolService Inherits AbstractGoToDefinitionSymbolService @@ -20,7 +21,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GoToDefinition Public Sub New() End Sub - Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol + Protected Overrides Async Function FindRelatedExplicitlyDeclaredSymbolAsync(project As Project, symbol As ISymbol, cancellationToken As CancellationToken) As Task(Of ISymbol) + Dim compilation = Await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(False) Return symbol.FindRelatedExplicitlyDeclaredSymbol(compilation) End Function diff --git a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb index 2cadad82d13ef..b91b722459024 100644 --- a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb +++ b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb @@ -3,8 +3,8 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports System.Diagnostics.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Inherits VisualBasicInvertIfCodeRefactoringProvider(Of MultiLineIfBlockSyntax) - + Public Sub New() End Sub diff --git a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb index 2c2325b37ca3a..a83e71dd19c96 100644 --- a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb +++ b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb @@ -3,8 +3,8 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports System.Diagnostics.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Inherits VisualBasicInvertIfCodeRefactoringProvider(Of SingleLineIfStatementSyntax) - + Public Sub New() End Sub diff --git a/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb b/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb index b7bbdec9277cd..0d6a04128eb5a 100644 --- a/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb +++ b/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb @@ -11,6 +11,7 @@ Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.MetadataAsSource Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Imports Microsoft.CodeAnalysis.VisualBasic.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -63,8 +64,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MetadataAsSource Return document.WithSyntaxRoot(newSyntaxRoot) End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) - Return _memberSeparationRule.Concat(Formatter.GetDefaultFormattingRules(document)) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) + Dim coreRules = Formatter.GetDefaultFormattingRules(document) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(1 + coreRules.Length) + result.Add(_memberSeparationRule) + result.AddRange(coreRules) + Return result.MoveToImmutable() End Function Protected Overrides Function GetReducers() As ImmutableArray(Of AbstractReducer) diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 32aae58938712..244c4a39c372c 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.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.Collections.Immutable Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading @@ -33,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Return Utilities.GetNodeToRemove(identifier) End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) Return Nothing End Function diff --git a/src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb b/src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb index bed592676cd94..ab1ada5e8801e 100644 --- a/src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb +++ b/src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb @@ -8,6 +8,7 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.AddConstructorParametersFromMembers.AddConstructorParametersFromMembersCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.AddConstructorParametersFromMembers + Public Class AddConstructorParametersFromMembersTests diff --git a/src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb b/src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb index 7c104952fd9aa..7fc1ca8f6de43 100644 --- a/src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb +++ b/src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb @@ -6,7 +6,7 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.AddDebuggerDisplay.VisualBasicAddDebuggerDisplayCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.AddDebuggerDisplay - + Public NotInheritable Class AddDebuggerDisplayTests diff --git a/src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb b/src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb index 41532d1e5d868..7e453bdcef496 100644 --- a/src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb +++ b/src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb @@ -7,9 +7,9 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.ConvertCast.VisualBasicConvertDirectCastToTryCastCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertCast + Public Class ConvertDirectCastToTryCastTests - Public Async Function ConvertFromDirectCastToTryCast() As Task Dim markup = " diff --git a/src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb b/src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb index 93c6379de90eb..3bb8bd62e4d8c 100644 --- a/src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb +++ b/src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb @@ -6,9 +6,9 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.ConvertConversionOperators.VisualBasicConvertTryCastToDirectCastCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertCast + Public Class ConvertTryCastToDirectCastTests - Public Async Function ConvertFromTryCastToDirectCast() As Task Dim markup = diff --git a/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb b/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb index 24324c5072dc5..c05675cb2265a 100644 --- a/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb +++ b/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb @@ -6,6 +6,7 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString.VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertToInterpolatedString + Public Class ConvertConcatenationToInterpolatedStringTests diff --git a/src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb b/src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb index 7509c513e45bc..e1cfe17d143e7 100644 --- a/src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb +++ b/src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb @@ -13,11 +13,12 @@ Imports VerifyRefactoring = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions. Microsoft.CodeAnalysis.GenerateDefaultConstructors.GenerateDefaultConstructorsCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.GenerateDefaultConstructors + Public Class GenerateDefaultConstructorsTests Private Shared Async Function TestRefactoringAsync(source As String, fixedSource As String, Optional index As Integer = 0) As Task Await TestRefactoringOnlyAsync(source, fixedSource, index) - await TestCodeFixMissingAsync(source) + Await TestCodeFixMissingAsync(source) End Function Private Shared Async Function TestRefactoringOnlyAsync(source As String, fixedSource As String, Optional index As Integer = 0) As Task diff --git a/src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb b/src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb index b4ade51d6f561..634897519851b 100644 --- a/src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb +++ b/src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb @@ -11,6 +11,7 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.IntroduceParameter.VisualBasicIntroduceParameterCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.IntroduceParameter + Public Class IntroduceParameterTests diff --git a/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb index 03cadc892d57f..de65c9f26ff25 100644 --- a/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb +++ b/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb @@ -2,22 +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. -Imports Microsoft.CodeAnalysis.CodeRefactorings -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Microsoft.CodeAnalysis.VisualBasic.InvertIf +Imports Microsoft.CodeAnalysis.Testing +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertMultiLineIfCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertIf - + Public Class InvertMultiLineIfTests - Inherits AbstractVisualBasicCodeActionTest_NoEditor - - Protected Overrides Function CreateCodeRefactoringProvider(workspace As TestWorkspace, parameters As TestParameters) As CodeRefactoringProvider - Return New VisualBasicInvertMultiLineIfCodeRefactoringProvider() + Private Shared Async Function TestInsideSubAsync(initial As String, expected As String, Optional languageVersion As LanguageVersion = LanguageVersion.Latest) As Task + Await TestAsync(CreateTreeText(initial), CreateTreeText(expected), languageVersion) End Function - Public Async Function TestFixOneAsync(initial As String, expected As String, Optional parseOptions As ParseOptions = Nothing) As Task - Await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected), parseOptions:=parseOptions) + Private Shared Async Function TestAsync(initial As String, expected As String, Optional languageVersion As LanguageVersion = LanguageVersion.Latest) As Task + Await New VerifyVB.Test With + { + .TestCode = initial, + .FixedCode = expected, + .LanguageVersion = languageVersion, + .CompilerDiagnostics = CompilerDiagnostics.None + }.RunAsync() End Function Public Shared Function CreateTreeText(initial As String) As String @@ -55,7 +58,7 @@ End Module Public Async Function TestMultiLineIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() @@ -74,7 +77,7 @@ End Module Public Async Function TestElseIf() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = " Sub Main() If a Then @@ -85,12 +88,14 @@ Sub Main() cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestKeepElseIfKeyword() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() If a Then @@ -101,12 +106,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnIfElseIfElse() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() I[||]f a Then @@ -117,12 +124,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestSelection() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [|If a Then aMethod() @@ -141,7 +150,7 @@ End Module") Public Async Function TestDoesNotOverlapHiddenPosition1() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() #End ExternalSource @@ -170,7 +179,7 @@ End Module") Public Async Function TestDoesNotOverlapHiddenPosition2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() #ExternalSource File.vb 1 @@ -197,7 +206,7 @@ End Module") Public Async Function TestMissingOnOverlapsHiddenPosition1() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -208,12 +217,14 @@ End Module") bMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition2() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() If a Then @@ -226,12 +237,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition3() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -244,12 +257,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition4() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -260,12 +275,14 @@ End Module") #End ExternalSource End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition5() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -276,12 +293,14 @@ End Module") #End ExternalSource End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition6() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -292,12 +311,14 @@ End Module") bMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMultipleStatementsMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -324,7 +345,7 @@ End Module") Public Async Function TestTriviaAfterMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -347,7 +368,7 @@ End Module") Public Async Function TestKeepExplicitLineContinuationTriviaMethod() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() I[||]f a And b _ @@ -372,7 +393,7 @@ End Module") Public Async Function TestKeepTriviaInStatementsInMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -400,7 +421,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -425,7 +446,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x() As String @@ -450,7 +471,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero4() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x() As String @@ -475,7 +496,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero5() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -500,7 +521,7 @@ End Module") Public Async Function TestDoesNotSimplifyToLengthEqualsZero() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -525,7 +546,7 @@ End Module") Public Async Function TestDoesNotSimplifyToLengthEqualsZero2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -552,7 +573,7 @@ End Module") Public Async Function TestColonAfterSingleLineIfWithEmptyElse() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() ' Invert If @@ -570,7 +591,7 @@ End Module") Public Async Function TestOnlyOnElseIf() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main(args As String()) If False Then @@ -581,12 +602,14 @@ End Module") Console.WriteLine(""a"") End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestOnConditionOfMultiLineIfStatement() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) If [||]False Then @@ -611,12 +634,12 @@ End Module") Public Async Function TestDoNotRemoveTypeCharactersDuringComplexification() As Task Dim markup = - +" Imports System Module Program Sub Main() Goo(Function(take) - [||]If True Then Console.WriteLine("true") Else Console.WriteLine("false") + [||]If True Then Console.WriteLine(""true"") Else Console.WriteLine(""false"") take$.ToString() Return Function() 1 End Function) @@ -626,15 +649,15 @@ Imports System Sub Goo(Of T)(x As Func(Of Integer, T)) End Sub End Module - +" Dim expected = - +" Imports System Module Program Sub Main() Goo(Function(take) - If False Then Console.WriteLine("false") Else Console.WriteLine("true") + If False Then Console.WriteLine(""false"") Else Console.WriteLine(""true"") take$.ToString() Return Function() 1 End Function) @@ -644,14 +667,14 @@ Imports System Sub Goo(Of T)(x As Func(Of Integer, T)) End Sub End Module - +" Await TestAsync(markup, expected) End Function Public Async Function InvertIfWithoutStatements() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -677,7 +700,7 @@ end class") Public Async Function InvertIfWithOnlyComment() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -706,7 +729,7 @@ end class") Public Async Function InvertIfWithoutElse() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -730,7 +753,7 @@ end class") Public Async Function TestMultiLineTypeOfIs_VB12() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a Is String Then aMethod() @@ -744,12 +767,12 @@ end class") Else aMethod() End If -", VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.VisualBasic12)) +", LanguageVersion.VisualBasic12) End Function Public Async Function TestMultiLineTypeOfIs_VB14() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a Is String Then aMethod() @@ -763,12 +786,12 @@ end class") Else aMethod() End If -", VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.VisualBasic14)) +", LanguageVersion.VisualBasic14) End Function Public Async Function TestMultiLineTypeOfIsNot() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a IsNot String Then aMethod() @@ -788,7 +811,7 @@ end class") Public Async Function PreserveSpace() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) @@ -819,7 +842,7 @@ end class") Public Async Function PreserveSpace_WithComments() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) @@ -858,7 +881,7 @@ end class") Public Async Function PreserveSpace_NoTrivia() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) diff --git a/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb index cb49815aa7f34..41ad87bbb660e 100644 --- a/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb +++ b/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb @@ -2,22 +2,24 @@ ' 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.CodeRefactorings -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Microsoft.CodeAnalysis.VisualBasic.InvertIf +Imports Microsoft.CodeAnalysis.Testing +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertSingleLineIfCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertIf - + Public Class InvertSingleLineIfTests - Inherits AbstractVisualBasicCodeActionTest_NoEditor - - Protected Overrides Function CreateCodeRefactoringProvider(workspace As TestWorkspace, parameters As TestParameters) As CodeRefactoringProvider - Return New VisualBasicInvertSingleLineIfCodeRefactoringProvider() + Private Shared Async Function TestInsideSubAsync(initial As String, expected As String) As Task + Await TestAsync(CreateTreeText(initial), CreateTreeText(expected)) End Function - Public Async Function TestFixOneAsync(initial As String, expected As String) As Task - Await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected)) + Private Shared Async Function TestAsync(initial As String, expected As String) As Task + Await New VerifyVB.Test With + { + .TestCode = initial, + .FixedCode = expected, + .CompilerDiagnostics = CompilerDiagnostics.None + }.RunAsync() End Function Public Shared Function CreateTreeText(initial As String) As String @@ -55,7 +57,7 @@ End Module Public Async Function TestAnd() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a And b Then aMethod() Else bMethod() ", @@ -67,7 +69,7 @@ End Module Public Async Function TestAddEmptyArgumentListIfNeeded() As Task Dim markup = - +" Module A Sub Main() [||]If True Then : Goo : Goo @@ -77,14 +79,14 @@ Module A Sub Goo() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestAndAlso() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a AndAlso b Then aMethod() Else bMethod() ", @@ -95,7 +97,7 @@ End Module Public Async Function TestCall() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a.Goo() Then aMethod() Else bMethod() ", @@ -106,7 +108,7 @@ End Module Public Async Function TestNotIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a Then aMethod() Else bMethod() ", @@ -117,7 +119,7 @@ End Module Public Async Function TestTrueLiteral() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If True Then aMethod() Else bMethod() ", @@ -128,7 +130,7 @@ End Module Public Async Function TestFalseLiteral() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If False Then aMethod() Else bMethod() ", @@ -139,7 +141,7 @@ End Module Public Async Function TestEquals() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a = b Then aMethod() Else bMethod() ", @@ -150,7 +152,7 @@ End Module Public Async Function TestNotEquals() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a <> b Then aMethod() Else bMethod() ", @@ -161,7 +163,7 @@ End Module Public Async Function TestLessThan() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a < b Then aMethod() Else bMethod() ", @@ -172,7 +174,7 @@ End Module Public Async Function TestLessThanOrEqual() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a <= b Then aMethod() Else bMethod() ", @@ -183,7 +185,7 @@ End Module Public Async Function TestGreaterThan() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a > b Then aMethod() Else bMethod() ", @@ -194,7 +196,7 @@ End Module Public Async Function TestGreaterThanOrEqual() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a >= b Then aMethod() Else bMethod() ", @@ -205,7 +207,7 @@ End Module Public Async Function TestIs() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " Dim myObject As New Object Dim thisObject = myObject @@ -222,7 +224,7 @@ End Module Public Async Function TestIsNot() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " Dim myObject As New Object Dim thisObject = myObject @@ -239,7 +241,7 @@ End Module Public Async Function TestOr() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Or b Then aMethod() Else bMethod() ", @@ -250,7 +252,7 @@ End Module Public Async Function TestOrElse() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a OrElse b Then aMethod() Else bMethod() ", @@ -261,7 +263,7 @@ End Module Public Async Function TestOr2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not a Or Not b Then aMethod() Else bMethod() ", @@ -272,7 +274,7 @@ End Module Public Async Function TestOrElse2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not a OrElse Not b Then aMethod() Else bMethod() ", @@ -283,7 +285,7 @@ End Module Public Async Function TestAnd2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a And Not b Then aMethod() Else bMethod() ", @@ -294,7 +296,7 @@ End Module Public Async Function TestAndAlso2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a AndAlso Not b Then aMethod() Else bMethod() ", @@ -305,7 +307,7 @@ End Module Public Async Function TestXor() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f a Xor b Then aMethod() Else bMethod() ", @@ -317,7 +319,7 @@ End Module Public Async Function TestXor2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not (a Xor b) Then aMethod() Else bMethod() ", @@ -328,7 +330,7 @@ End Module Public Async Function TestNested() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If (((a = b) AndAlso (c <> d)) OrElse ((e < f) AndAlso (Not g))) Then aMethod() Else bMethod() ", @@ -340,7 +342,7 @@ End Module Public Async Function TestEscapeKeywordsIfNeeded1() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -350,15 +352,15 @@ Module Program Sub Take() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded2() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -368,15 +370,15 @@ Module Program Sub Ascending() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded3() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -386,15 +388,15 @@ Module Program Sub Ascending() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded4() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -402,26 +404,15 @@ Module Program Take: Return End Sub End Module - - - Dim expected = - -Imports System.Linq -Module Program - Sub Main() - If False Then Console.WriteLine() Else Dim q = From x In "" -[Take]: Return - End Sub -End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded5() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -431,14 +422,14 @@ Module Program Sub Take() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestSelection() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [|If a And b Then aMethod() Else bMethod()|] ", @@ -449,7 +440,7 @@ End Module Public Async Function TestMultipleStatementsSingleLineIfStatement() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " If[||] a Then aMethod() : bMethod() Else cMethod() : d() ", @@ -460,7 +451,7 @@ End Module Public Async Function TestTriviaAfterSingleLineIfStatement() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() Else bMethod() ' I will stay put ", @@ -470,7 +461,7 @@ End Module End Function Public Async Function TestParenthesizeForLogicalExpressionPrecedence() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Sub Main() I[||]f a AndAlso b Or c Then aMethod() Else bMethod() End Sub @@ -483,7 +474,7 @@ End Module") Public Async Function TestParenthesizeComparisonOperands() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If 0 <= .GetHashCode Then aMethod() Else bMethod() ", @@ -496,7 +487,7 @@ End Module") Public Async Function TestNestedSingleLineIfs() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() ' Invert the 1st If @@ -513,18 +504,20 @@ End Module") Public Async Function TestTryToParenthesizeAwkwardSyntaxInsideSingleLineLambdaMethod() As Task - Await TestMissingAsync( + Dim markup = "Module Program Sub Main() ' Invert If Dim x = Sub() I[||]f True Then Dim y Else Console.WriteLine(), z = 1 End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestOnConditionOfSingleLineIf() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) If T[||]rue Then Return Else Console.WriteLine(""a"") @@ -541,42 +534,42 @@ End Module") Public Async Function TestImplicitLineContinuationBeforeClosingParenIsRemoved() As Task Dim markup = - +" [||]If (True OrElse True ) Then Else End If - +" Dim expected = - +" If False AndAlso False Then Else End If - +" - Await TestAsync(markup, expected) + Await TestInsideSubAsync(markup, expected) End Function Public Async Function TestParenthesizeToKeepParseTheSame1() As Task Dim markup = - +" Module Program Sub Main() - [||]If 0 >= <x/>.GetHashCode Then Console.WriteLine(1) Else Console.WriteLine(2) + [||]If 0 >= .GetHashCode Then Console.WriteLine(1) Else Console.WriteLine(2) End Sub End Module - +" Dim expected = - +" Module Program Sub Main() - If 0 < (<x/>.GetHashCode) Then Console.WriteLine(2) Else Console.WriteLine(1) + If 0 < (.GetHashCode) Then Console.WriteLine(2) Else Console.WriteLine(1) End Sub End Module - +" Await TestAsync(markup, expected) End Function @@ -584,7 +577,7 @@ End Module Public Async Function TestParenthesizeToKeepParseTheSame2() As Task Dim markup = - +" Module Program Sub Main() Select Nothing @@ -592,14 +585,14 @@ Module Program End Select End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestSingleLineIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() Else bMethod() ", @@ -610,7 +603,7 @@ End Module Public Async Function TestWithMissingTrueStatementWithinUsing() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub M(Disposable As IDisposable) Dim x = True diff --git a/src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb b/src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb index 98c7f2af1f35a..c94569a532656 100644 --- a/src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb +++ b/src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb @@ -15,7 +15,7 @@ Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBas Microsoft.CodeAnalysis.VisualBasic.ReplaceConditionalWithStatements.VisualBasicReplaceConditionalWithStatementsCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ReplaceConditionalWithStatements - + Public Class ReplaceConditionalWithStatementsTests Public Async Function TestAssignment_ObjectType() As Task diff --git a/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb b/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb index 67e10d591bdcc..9ee02703f1680 100644 --- a/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb +++ b/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics +Imports Microsoft.CodeAnalysis.Remote.Testing Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.SimplifyTypeNames Imports Microsoft.CodeAnalysis.VisualBasic.SimplifyTypeNames @@ -1821,7 +1822,7 @@ End Module Await TestInRegularAndScriptAsync(source.Value, expected.Value) - Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition()) + Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition().WithTestHostParts(TestHost.OutOfProcess)) Dim diagnosticAndFixes = Await GetDiagnosticAndFixesAsync(workspace, New TestParameters()) Dim span = diagnosticAndFixes.Item1.First().Location.SourceSpan Assert.NotEqual(span.Start, 0) @@ -1869,7 +1870,7 @@ End Namespace Await TestInRegularAndScriptAsync(source.Value, expected.Value) - Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition()) + Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition().WithTestHostParts(TestHost.OutOfProcess)) Dim diagnosticAndFixes = Await GetDiagnosticAndFixesAsync(workspace, New TestParameters()) Dim span = diagnosticAndFixes.Item1.First().Location.SourceSpan Assert.Equal(span.Start, expected.Value.ReplaceLineEndings(vbLf).IndexOf("new C", StringComparison.Ordinal) + 4) @@ -1903,7 +1904,7 @@ End Module Await TestInRegularAndScriptAsync(source.Value, expected.Value) - Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition()) + Using workspace = TestWorkspace.CreateVisualBasic(source.Value, composition:=GetComposition().WithTestHostParts(TestHost.OutOfProcess)) Dim diagnosticAndFixes = Await GetDiagnosticAndFixesAsync(workspace, New TestParameters()) Dim span = diagnosticAndFixes.Item1.First().Location.SourceSpan Assert.Equal(span.Start, expected.Value.ReplaceLineEndings(vbLf).IndexOf("Console.WriteLine(""goo"")", StringComparison.Ordinal)) diff --git a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj index d62e1c2961bef..6ec1177df567e 100644 --- a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj @@ -49,7 +49,6 @@ - diff --git a/src/Scripting/CSharpTest/CommandLineRunnerTests.cs b/src/Scripting/CSharpTest/CommandLineRunnerTests.cs index 0d50eb1e902cc..bdf2e78cd6788 100644 --- a/src/Scripting/CSharpTest/CommandLineRunnerTests.cs +++ b/src/Scripting/CSharpTest/CommandLineRunnerTests.cs @@ -121,6 +121,9 @@ select x * x "); runner.RunInteractive(); + var iteratorType = RuntimeUtilities.IsCoreClr9OrHigherRuntime + ? "ArrayWhereSelectIterator" + : "WhereSelectArrayIterator"; AssertEx.AssertEqualToleratingWhitespaceDifferences( $@"{LogoAndHelpPrompt} > async Task GetStuffAsync() @@ -133,7 +136,7 @@ select x * x > from x in await GetStuffAsync() . where x > 2 . select x * x -Enumerable.WhereSelectArrayIterator {{ 9, 16, 25 }} +Enumerable.{iteratorType} {{ 9, 16, 25 }} > ", runner.Console.Out.ToString()); AssertEx.AssertEqualToleratingWhitespaceDifferences( diff --git a/src/Scripting/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj b/src/Scripting/CSharpTest/Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests.csproj index 5e8ced49b9982..3c7a527a29e20 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 - $(NetRoslyn);net472 + $(NetRoslynNext);net472 diff --git a/src/Scripting/CSharpTest/ObjectFormatterTests.cs b/src/Scripting/CSharpTest/ObjectFormatterTests.cs index b4ce5c4f787f5..07201a7ada581 100644 --- a/src/Scripting/CSharpTest/ObjectFormatterTests.cs +++ b/src/Scripting/CSharpTest/ObjectFormatterTests.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting.Hosting; using Microsoft.CodeAnalysis.Scripting.Hosting.UnitTests; +using Microsoft.CodeAnalysis.Test.Utilities; using ObjectFormatterFixtures; using Roslyn.Test.Utilities; using Xunit; @@ -507,7 +508,10 @@ public void DebuggerProxy_FrameworkTypes_IEnumerable_Exception() return i < 7; }); str = s_formatter.FormatObject(obj, SingleLineOptions); - Assert.Equal("Enumerable.WhereEnumerableIterator { 0, 1, 2, 3, 4, ! ... }", str); + var iteratorType = RuntimeUtilities.IsCoreClr9OrHigherRuntime + ? "IEnumerableWhereIterator" + : "WhereEnumerableIterator"; + Assert.Equal($"Enumerable.{iteratorType} {{ 0, 1, 2, 3, 4, ! ... }}", str); } [Fact] diff --git a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj index d3e2de0ff5753..e76457a1358fc 100644 --- a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj +++ b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj @@ -22,7 +22,6 @@ - diff --git a/src/Tools/BuildBoss/ProjectCheckerUtil.cs b/src/Tools/BuildBoss/ProjectCheckerUtil.cs index 30169fa97809f..f42d30b8e6ace 100644 --- a/src/Tools/BuildBoss/ProjectCheckerUtil.cs +++ b/src/Tools/BuildBoss/ProjectCheckerUtil.cs @@ -247,6 +247,7 @@ private bool CheckTargetFrameworks(TextWriter textWriter) case "net472": case "netstandard2.0": case "$(NetRoslyn)": + case "$(NetRoslynNext)": case "$(NetRoslynSourceBuild)": case "$(NetRoslynToolset)": case "$(NetRoslynAll)": diff --git a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs index 23a9bfe717622..f8b81391c6405 100644 --- a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs +++ b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting internal static class OmniSharpFormatter { public static Task FormatAsync(Document document, IEnumerable? spans, OmniSharpSyntaxFormattingOptionsWrapper options, CancellationToken cancellationToken) - => Formatter.FormatAsync(document, spans, options.CleanupOptions.FormattingOptions, rules: null, cancellationToken); + => Formatter.FormatAsync(document, spans, options.CleanupOptions.FormattingOptions, rules: default, cancellationToken); public static async Task OrganizeImportsAsync(Document document, OmniSharpOrganizeImportsOptionsWrapper options, CancellationToken cancellationToken) { diff --git a/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs index 93dc75dd8acda..e3c504f40fe0a 100644 --- a/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor /// internal abstract class AbstractRazorLanguageServerFactoryWrapper { - internal abstract IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, IRazorTestCapabilitiesProvider capabilitiesProvider, HostServices hostServices); + internal abstract IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider capabilitiesProvider, HostServices hostServices); internal abstract DocumentInfo CreateDocumentInfo( DocumentId id, @@ -31,6 +31,6 @@ internal abstract DocumentInfo CreateDocumentInfo( /// /// Supports the creation of a Roslyn LSP server for functional tests /// - internal abstract void AddJsonConverters(JsonSerializer jsonSerializer); + internal abstract void AddJsonConverters(JsonSerializerOptions options); } } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs b/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs index 425cfce0e740c..8891b52c7c275 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs @@ -4,13 +4,13 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; @@ -74,12 +74,8 @@ private void InitializeRazor(ClientCapabilities clientCapabilities, RequestConte // UIContext will already be active, so this method will be immediately called on the new instance. if (cancellationToken.IsCancellationRequested) return; - var serializer = new JsonSerializer(); - serializer.AddVSInternalExtensionConverters(); - var serializerSettings = new JsonSerializerSettings { Converters = serializer.Converters }; - // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL - var serializedClientCapabilities = JsonConvert.SerializeObject(clientCapabilities, serializerSettings); + var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); var razorCohostClientLanguageServerManager = new RazorCohostClientLanguageServerManager(clientLanguageServerManager!); var requestContext = new RazorCohostRequestContext(context); diff --git a/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs b/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs new file mode 100644 index 0000000000000..85c36e8c54a15 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.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; +using System.Composition; +using System.Diagnostics; +using System.Reflection; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Composition; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + [Export(typeof(IAnalyzerAssemblyResolver)), Shared] + internal class RazorAnalyzerAssemblyResolver : IAnalyzerAssemblyResolver + { + private static Func? s_assemblyResolver; + + /// + /// We use this as a heuristic to catch a case where we set the resolver too + /// late and the resolver has already been asked to resolve a razor assembly. + /// + /// Note this isn't perfectly accurate but is only used to trigger an assert + /// in debug builds. + /// + private static bool s_razorRequested = false; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorAnalyzerAssemblyResolver() + { + } + + internal static Func? AssemblyResolver + { + get => s_assemblyResolver; + set + { + Debug.Assert(s_assemblyResolver == null, "Assembly resolver should not be set multiple times."); + Debug.Assert(!s_razorRequested, "A razor assembly has already been requested before setting the resolver."); + s_assemblyResolver = value; + } + } + + public Assembly? ResolveAssembly(AssemblyName assemblyName) + { + s_razorRequested |= assemblyName.FullName.Contains("Razor"); + return s_assemblyResolver?.Invoke(assemblyName); + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs index 989fc0e911cfb..81cfa6493e4cd 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.VisualStudio.Composition; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -33,10 +33,10 @@ public RazorLanguageServerFactoryWrapper(ILanguageServerFactory languageServerFa _languageServerFactory = languageServerFactory; } - internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) + internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) { - var capabilitiesProvider = new RazorCapabilitiesProvider(razorCapabilitiesProvider); - var languageServer = _languageServerFactory.Create(jsonRpc, jsonSerializer, capabilitiesProvider, WellKnownLspServerKinds.RazorLspServer, NoOpLspLogger.Instance, hostServices); + var capabilitiesProvider = new RazorCapabilitiesProvider(razorCapabilitiesProvider, options); + var languageServer = _languageServerFactory.Create(jsonRpc, options, capabilitiesProvider, WellKnownLspServerKinds.RazorLspServer, NoOpLspLogger.Instance, hostServices); return new RazorLanguageServerTargetWrapper(languageServer); } @@ -65,27 +65,29 @@ internal override DocumentInfo CreateDocumentInfo( .WithDocumentServiceProvider(documentServiceProvider); } - internal override void AddJsonConverters(JsonSerializer jsonSerializer) + internal override void AddJsonConverters(JsonSerializerOptions options) { - VSInternalExtensionUtilities.AddVSInternalExtensionConverters(jsonSerializer); + VSInternalExtensionUtilities.AddVSInternalExtensionConverters(options); } private class RazorCapabilitiesProvider : ICapabilitiesProvider { private readonly IRazorTestCapabilitiesProvider _razorTestCapabilitiesProvider; + private readonly JsonSerializerOptions _options; - public RazorCapabilitiesProvider(IRazorTestCapabilitiesProvider razorTestCapabilitiesProvider) + public RazorCapabilitiesProvider(IRazorTestCapabilitiesProvider razorTestCapabilitiesProvider, JsonSerializerOptions options) { _razorTestCapabilitiesProvider = razorTestCapabilitiesProvider; + _options = options; } public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) { // To avoid exposing types from MS.VS.LanguageServer.Protocol types we serialize and deserialize the capabilities // so we can just pass string around. This is obviously not great for perf, but it is only used in Razor tests. - var clientCapabilitiesJson = JsonConvert.SerializeObject(clientCapabilities); + var clientCapabilitiesJson = JsonSerializer.Serialize(clientCapabilities, _options); var serverCapabilitiesJson = _razorTestCapabilitiesProvider.GetServerCapabilitiesJson(clientCapabilitiesJson); - var serverCapabilities = JsonConvert.DeserializeObject(serverCapabilitiesJson); + var serverCapabilities = JsonSerializer.Deserialize(serverCapabilitiesJson, _options); if (serverCapabilities is null) { diff --git a/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs b/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs index a37288b57bf7e..bec4c319fe13a 100644 --- a/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs +++ b/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -21,7 +21,7 @@ public static object ToResolveData(object data, Uri uri) public static (object? data, Uri? uri) FromResolveData(object? requestData) { Contract.ThrowIfNull(requestData); - var resolveData = ((JToken)requestData).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)requestData); return (resolveData?.Data, resolveData?.Document.Uri); } @@ -35,9 +35,9 @@ internal static object ToCachedResolveData(object data, Uri uri, ResolveDataCach internal static (object? data, Uri? uri) FromCachedResolveData(object? lspData, ResolveDataCache resolveDataCache) { DataIdResolveData? resolveData; - if (lspData is JToken token) + if (lspData is JsonElement token) { - resolveData = token.ToObject(); + resolveData = JsonSerializer.Deserialize(token); Assumes.Present(resolveData); } else diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index a56836798245d..5748e2260fd13 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.CSharp; @@ -20,13 +21,6 @@ internal static class GrammarGenerator { public static string Run(List types) { - // Syntax.xml refers to a special pseudo-element 'Modifier'. Synthesize that for the grammar. - var modifiers = GetMembers() - .Select(m => m + "Keyword").Where(n => GetSyntaxKind(n) != SyntaxKind.None) - .Select(n => new Kind { Name = n }).ToList(); - - types.Add(new Node { Name = "Modifier", Children = { new Field { Type = "SyntaxToken", Kinds = modifiers } } }); - var rules = types.ToDictionary(n => n.Name, _ => new List()); foreach (var type in types) { @@ -53,38 +47,50 @@ public static string Run(List types) var originalLastFieldKinds = lastField.Kinds.ToList(); for (int i = 0; i < originalFirstFieldKinds.Count; i++) { - firstField.Kinds = new List { originalFirstFieldKinds[i] }; - lastField.Kinds = new List { originalLastFieldKinds[i] }; - rules[type.Name].Add(HandleChildren(type.Children)); + firstField.Kinds = [originalFirstFieldKinds[i]]; + lastField.Kinds = [originalLastFieldKinds[i]]; + rules[type.Name].Add(Sequence(type.Children.Select(ToProduction))); } } else { for (int i = 0; i < originalFirstFieldKinds.Count; i++) { - firstField.Kinds = new List { originalFirstFieldKinds[i] }; - rules[type.Name].Add(HandleChildren(type.Children)); + firstField.Kinds = [originalFirstFieldKinds[i]]; + rules[type.Name].Add(Sequence(type.Children.Select(ToProduction))); } } } else { - rules[type.Name].Add(HandleChildren(type.Children)); + rules[type.Name].Add(Sequence(type.Children.Select(ToProduction))); } } } + // Add some rules not present in Syntax.xml. + AddLexicalRules(rules); + // The grammar will bottom out with certain lexical productions. Create rules for these. var lexicalRules = rules.Values.SelectMany(ps => ps).SelectMany(p => p.ReferencedRules) .Where(r => !rules.TryGetValue(r, out var productions) || productions.Count == 0).ToArray(); foreach (var name in lexicalRules) - rules[name] = new List { new Production("/* see lexical specification */") }; + rules[name] = [new("/* see lexical specification */")]; var seen = new HashSet(); // Define a few major sections to help keep the grammar file naturally grouped. - var majorRules = ImmutableArray.Create( - "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax"); + List majorRules = [ + "CompilationUnitSyntax", + "MemberDeclarationSyntax", + "TypeSyntax", + "StatementSyntax", + "ExpressionSyntax", + "XmlNodeSyntax", + "StructuredTriviaSyntax", + // Place all syntax tokens at the end to keep them out of the way. + "SyntaxToken", + .. rules["SyntaxToken"].SelectMany(r => r.ReferencedRules)]; var result = "// " + Environment.NewLine + "grammar csharp;" + Environment.NewLine; @@ -101,7 +107,7 @@ void processRule(string name, ref string result) // Order the productions to keep us independent from whatever changes happen in Syntax.xml. var sorted = rules[name].OrderBy(v => v); result += Environment.NewLine + RuleReference(name).Text + Environment.NewLine + " : " + - string.Join(Environment.NewLine + " | ", sorted) + Environment.NewLine + " ;" + Environment.NewLine; + string.Join(Environment.NewLine + " | ", sorted) + Environment.NewLine + " ;" + Environment.NewLine; // Now proceed in depth-first fashion through the referenced rules to keep related rules // close by. Don't recurse into major-sections to help keep them separated in grammar file. @@ -113,14 +119,209 @@ void processRule(string name, ref string result) } } - private static Production Join(string delim, IEnumerable productions) - => new Production(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); + private static void AddLexicalRules(Dictionary> rules) + { + addUtf8Rules(); + addTokenRules(); + addIdentifierRules(); + addRealLiteralRules(); + addNumericLiteralRules(); + addIntegerLiteralRules(); + addEscapeSequenceRules(); + addStringLiteralRules(); + addCharacterLiteralRules(); + + void addUtf8Rules() + { + var utf8Suffix = Choice(anyCasing("U8")); + rules.Add("Utf8StringLiteralToken", [Sequence([RuleReference("StringLiteralToken"), utf8Suffix])]); + rules.Add("Utf8MultiLineRawStringLiteralToken", [Sequence([RuleReference("MultiLineRawStringLiteralToken"), utf8Suffix])]); + rules.Add("Utf8SingleLineRawStringLiteralToken", [Sequence([RuleReference("SingleLineRawStringLiteralToken"), utf8Suffix])]); + } + + void addTokenRules() + { + rules["SyntaxToken"].AddRange([RuleReference("IdentifierToken"), RuleReference("Keyword"), RuleReference("NumericLiteralToken"), RuleReference("CharacterLiteralToken"), RuleReference("StringLiteralToken"), RuleReference("OperatorToken"), RuleReference("PunctuationToken")]); + + var modifierWords = GetMembers() + .Where(n => GetSyntaxKind(n + "Keyword") != SyntaxKind.None) + .Select(n => n.ToString().ToLower()); + rules.Add("Modifier", JoinWords(modifierWords)); + + var keywords = JoinWords(GetMembers().Where(k => SyntaxFacts.IsReservedKeyword(k)).Select(SyntaxFacts.GetText).Where(t => !modifierWords.Contains(t))); + keywords.Add(RuleReference("Modifier")); + rules.Add("Keyword", keywords); + + var operatorTokens = GetMembers().Where(m => SyntaxFacts.IsBinaryExpressionOperatorToken(m) || SyntaxFacts.IsPostfixUnaryExpression(m) || SyntaxFacts.IsPrefixUnaryExpression(m) || SyntaxFacts.IsAssignmentExpressionOperatorToken(m)); + rules.Add("OperatorToken", JoinWords(operatorTokens.Select(SyntaxFacts.GetText))); + + rules.Add("PunctuationToken", JoinWords(GetMembers() + .Where(m => SyntaxFacts.IsLanguagePunctuation(m) && !operatorTokens.Contains(m) && !m.ToString().StartsWith("Xml")) + .Select(SyntaxFacts.GetText))); + } + + void addIdentifierRules() + { + rules.Add("IdentifierToken", [Sequence([Text("@").Optional, RuleReference("IdentifierStartCharacter"), RuleReference("IdentifierPartCharacter")])]); + rules.Add("IdentifierStartCharacter", [RuleReference("LetterCharacter"), RuleReference("UnderscoreCharacter")]); + rules.Add("IdentifierPartCharacter", [RuleReference("LetterCharacter"), RuleReference("DecimalDigitCharacter"), RuleReference("ConnectingCharacter"), RuleReference("CombiningCharacter"), RuleReference("FormattingCharacter")]); + rules.Add("UnderscoreCharacter", [Text("_"), new("""'\\u005' /* unicode_escape_sequence for underscore */""")]); + rules.Add("LetterCharacter", [ + new("""/* [\p{L}\p{Nl}] category letter, all subcategories; category number, subcategory letter */"""), + new("unicode_escape_sequence /* only escapes for categories L & Nl allowed */")]); + + rules.Add("CombiningCharacter", [ + new("""/* [\p{Mn}\p{Mc}] category Mark, subcategories non-spacing and spacing combining */"""), + new("unicode_escape_sequence /* only escapes for categories Mn & Mc allowed */")]); + + rules.Add("DecimalDigitCharacter", [ + new("""/* [\p{Nd}] category number, subcategory decimal digit */"""), + new("unicode_escape_sequence /* only escapes for category Nd allowed */")]); + + rules.Add("ConnectingCharacter", [ + new("""/* [\p{Pc}] category Punctuation, subcategory connector */"""), + new("unicode_escape_sequence /* only escapes for category Pc allowed */")]); + + rules.Add("FormattingCharacter", [ + new("""/* [\p{Cf}] category Other, subcategory format. */"""), + new("unicode_escape_sequence /* only escapes for category Cf allowed */")]); + } + + void addRealLiteralRules() + { + var decimalDigitPlus = RuleReference("DecimalDigit").OneOrMany; + var exponentPart = RuleReference("ExponentPart"); + var exponentPartOpt = exponentPart.Optional; + var realTypeSuffix = RuleReference("RealTypeSuffix"); + var realTypeSuffixOpt = realTypeSuffix.Optional; + + rules.Add("RealLiteralToken", [ + Sequence([decimalDigitPlus, Text("."), decimalDigitPlus, exponentPartOpt, realTypeSuffixOpt]), + Sequence([Text("."), decimalDigitPlus, exponentPartOpt, realTypeSuffixOpt]), + Sequence([decimalDigitPlus, exponentPart, realTypeSuffixOpt]), + Sequence([decimalDigitPlus, realTypeSuffix]), + ]); + + rules.Add("ExponentPart", [Sequence([Choice(anyCasing("E")), Choice([Text("+"), Text("-")]).Optional, decimalDigitPlus])]); + rules.Add("RealTypeSuffix", [.. anyCasing("F"), .. anyCasing("D"), .. anyCasing("M")]); + } + + void addNumericLiteralRules() + { + rules.Add("NumericLiteralToken", [RuleReference("IntegerLiteralToken"), RuleReference("RealLiteralToken")]); + } + + void addIntegerLiteralRules() + { + var decimalDigit = RuleReference("DecimalDigit"); + var decimalDigitPlus = decimalDigit.OneOrMany; + var integerTypeSuffixOpt = RuleReference("IntegerTypeSuffix").Optional; + + rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); + rules.Add("DecimalIntegerLiteralToken", [Sequence([decimalDigitPlus, integerTypeSuffixOpt])]); + rules.Add("IntegerTypeSuffix", [.. anyCasing("U"), .. anyCasing("L"), .. anyCasing("UL"), .. anyCasing("LU")]); + rules.Add("DecimalDigit", [.. productionRange('0', '9')]); + rules.Add("HexadecimalDigit", [decimalDigit, .. productionRange('A', 'F'), .. productionRange('a', 'f')]); + rules.Add("HexadecimalIntegerLiteralToken", [Sequence([Choice([Text("0x"), Text("0X")]), RuleReference("HexadecimalDigit").OneOrMany, integerTypeSuffixOpt])]); + } + + void addEscapeSequenceRules() + { + var hexDigit = RuleReference("HexadecimalDigit"); + var hexDigitOpt = hexDigit.Optional; + + rules.Add("SimpleEscapeSequence", [Text(@"\'"), Text(@"\"""), Text(@"\\"), Text(@"\0"), Text(@"\a"), Text(@"\b"), Text(@"\f"), Text(@"\n"), Text(@"\r"), Text(@"\t"), Text(@"\v")]); + rules.Add("HexadecimalEscapeSequence", [Sequence([Text(@"\x"), hexDigit, .. repeat(hexDigitOpt, 3)])]); + rules.Add("UnicodeEscapeSequence", [Sequence([Text(@"\u"), .. repeat(hexDigit, 4)]), Sequence([Text(@"\U"), .. repeat(hexDigit, 8)])]); + } + + void addStringLiteralRules() + { + rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); + + rules.Add("RegularStringLiteralToken", [Sequence([Text("\""), RuleReference("RegularStringLiteralCharacter").ZeroOrMany, Text("\"")])]); + rules.Add("RegularStringLiteralCharacter", [RuleReference("SingleRegularStringLiteralCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); + rules.Add("SingleRegularStringLiteralCharacter", [new("""/* ~["\\\u000D\u000A\u0085\u2028\u2029] anything but ", \, and new_line_character */""")]); + + rules.Add("VerbatimStringLiteralToken", [Sequence([Text("@\""), RuleReference("VerbatimStringLiteralCharacter").ZeroOrMany, Text("\"")])]); + rules.Add("VerbatimStringLiteralCharacter", [RuleReference("SingleVerbatimStringLiteralCharacter"), RuleReference("QuoteEscapeSequence")]); + rules.Add("SingleVerbatimStringLiteralCharacter", [new("/* anything but quotation mark (U+0022) */")]); - private static Production HandleChildren(IEnumerable children, string delim = " ") - => Join(delim, children.Select(child => - child is Choice c ? HandleChildren(c.Children, delim: " | ").Parenthesize().Suffix("?", when: c.Optional) : - child is Sequence s ? HandleChildren(s.Children).Parenthesize() : - child is Field f ? HandleField(f).Suffix("?", when: f.IsOptional) : throw new InvalidOperationException())); + rules.Add("QuoteEscapeSequence", [Text("\"\"")]); + + rules.Add("InterpolatedMultiLineRawStringStartToken", [new(""""'$'+ '"""' '"'*"""")]); + rules.Add("InterpolatedRawStringEndToken", [new(""""'"""' '"'* /* must match number of quotes in raw_string_start_token */"""")]); + rules.Add("InterpolatedSingleLineRawStringStartToken", [new(""""'$'+ '"""' '"'*"""")]); + } + + void addCharacterLiteralRules() + { + rules.Add("CharacterLiteralToken", [Sequence([Text("'"), RuleReference("Character"), Text("'")])]); + rules.Add("Character", [RuleReference("SingleCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); + rules.Add("SingleCharacter", [new("""/* ~['\\\u000D\u000A\u0085\u2028\u2029] anything but ', \\, and new_line_character */""")]); + } + + IEnumerable productionRange(char start, char end) + { + for (char c = start; c <= end; c++) + yield return Text($"{c}"); + } + + IEnumerable repeat(Production production, int count) + => Enumerable.Repeat(production, count); + + IEnumerable anyCasing(string value) + { + var array = value.Select(c => char.IsLetter(c) ? [char.ToUpperInvariant(c), char.ToLowerInvariant(c)] : new[] { c }).ToArray(); + + var indices = new int[array.Length]; + var builder = new StringBuilder(); + do + { + for (var i = 0; i < value.Length; i++) + builder.Append(array[i][indices[i]]); + + yield return Text($"{builder}"); + builder.Clear(); + + for (var i = 0; i < indices.Length; i++) + { + if (++indices[i] < array[i].Length) + break; + + indices[i] = 0; + } + } + while (!indices.All(i => i == 0)); + } + } + + private static List JoinWords(IEnumerable strings) + => strings.Select(s => new Production($"""'{Escape(s)}'""")).ToList(); + + private static string Escape(string s) + => s.Replace(@"\", @"\\").Replace("'", @"\'"); + + private static Production Text(string value) + => new($"'{Escape(value)}'"); + + private static Production Join(IEnumerable productions, string delim) + => new(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); + + private static Production ToProduction(TreeTypeChild child) + => child switch + { + Choice c => Choice(c.Children.Select(ToProduction)).Suffix("?", when: c.Optional), + Sequence s => Sequence(s.Children.Select(ToProduction)).Parenthesize(), + Field f => HandleField(f).Suffix("?", when: f.IsOptional), + _ => throw new InvalidOperationException(), + }; + + private static Production Choice(IEnumerable productions, bool parenthesize = true) + => Join(productions, " | ").Parenthesize(parenthesize); + + private static Production Sequence(IEnumerable productions) + => Join(productions, " "); private static Production HandleField(Field field) // 'bool' fields are for a few properties we generate on DirectiveTrivia. They're not @@ -139,7 +340,7 @@ private static Production HandleSeparatedList(Field field, string elementType) private static Production HandleList(Field field, string elementType) => (elementType != "SyntaxToken" ? RuleReference(elementType) : - field.Name == "Commas" ? new Production("','") : + field.Name == "Commas" ? Text(",") : field.Name == "Modifiers" ? RuleReference("Modifier") : field.Name == "TextTokens" ? RuleReference(nameof(SyntaxKind.XmlTextLiteralToken)) : RuleReference(elementType)) .Suffix(field.MinCount == 0 ? "*" : "+"); @@ -147,11 +348,11 @@ private static Production HandleList(Field field, string elementType) private static Production HandleTokenField(Field field) => field.Kinds.Count == 0 ? HandleTokenName(field.Name) - : Join(" | ", field.Kinds.Select(k => HandleTokenName(k.Name))).Parenthesize(when: field.Kinds.Count >= 2); + : Choice(field.Kinds.Select(k => HandleTokenName(k.Name)), parenthesize: field.Kinds.Count >= 2); private static Production HandleTokenName(string tokenName) => GetSyntaxKind(tokenName) is var kind && kind == SyntaxKind.None ? RuleReference("SyntaxToken") : - SyntaxFacts.GetText(kind) is var text && text != "" ? new Production(text == "'" ? "'\\''" : $"'{text}'") : + SyntaxFacts.GetText(kind) is var text && text != "" ? Text(text) : tokenName.StartsWith("EndOf") ? new Production("") : tokenName.StartsWith("Omitted") ? new Production("/* epsilon */") : RuleReference(tokenName); @@ -162,32 +363,30 @@ private static IEnumerable GetMembers() where TEnum : struct, Enum => (IEnumerable)Enum.GetValues(typeof(TEnum)); private static Production RuleReference(string name) - => new Production( + => new( s_normalizationRegex.Replace(name.EndsWith("Syntax") ? name[..^"Syntax".Length] : name, "_").ToLower(), ImmutableArray.Create(name)); // Converts a PascalCased name into snake_cased name. - private static readonly Regex s_normalizationRegex = new Regex( - "(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", + private static readonly Regex s_normalizationRegex = new( + "(?<=[A-Z])(?=[A-Z][a-z0-9]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z0-9])(?=[^A-Za-z0-9])", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); } - internal readonly struct Production : IComparable + internal readonly struct Production( + string text, IEnumerable referencedRules = null) : IComparable { - public readonly string Text; - public readonly ImmutableArray ReferencedRules; - - public Production(string text, IEnumerable referencedRules = null) - { - Text = text; - ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; - } + public readonly string Text = text; + public readonly ImmutableArray ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; public override string ToString() => Text; - public int CompareTo(Production other) => StringComparer.Ordinal.Compare(this.Text, other.Text); - public Production Prefix(string prefix) => new Production(prefix + this, ReferencedRules); - public Production Suffix(string suffix, bool when = true) => when ? new Production(this + suffix, ReferencedRules) : this; + public int CompareTo(Production other) => StringComparer.OrdinalIgnoreCase.Compare(this.Text, other.Text); + public Production Prefix(string prefix) => new(prefix + this, ReferencedRules); + public Production Suffix(string suffix, bool when = true) => when ? new(this + suffix, ReferencedRules) : this; public Production Parenthesize(bool when = true) => when ? Prefix("(").Suffix(")") : this; + public Production Optional => Suffix("?"); + public Production ZeroOrMany => Suffix("*"); + public Production OneOrMany => Suffix("+"); } } diff --git a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs index d386add5b3e2e..9e67add1eaba5 100644 --- a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs +++ b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs @@ -2,9 +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.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -12,8 +11,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Test; 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; @@ -22,9 +19,7 @@ using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServices.DocumentOutline; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Threading; -using Moq; -using Newtonsoft.Json.Linq; +using StreamJsonRpc; using Xunit.Abstractions; using static Roslyn.Test.Utilities.AbstractLanguageServerProtocolTests; using IAsyncDisposable = System.IAsyncDisposable; @@ -49,7 +44,7 @@ protected class DocumentOutlineTestMocks : IAsyncDisposable private readonly IAsyncDisposable _disposable; internal DocumentOutlineTestMocks( - LanguageServiceBrokerCallback languageServiceBrokerCallback, + LanguageServiceBrokerCallback languageServiceBrokerCallback, IThreadingContext threadingContext, EditorTestWorkspace workspace, IAsyncDisposable disposable) @@ -61,7 +56,7 @@ internal DocumentOutlineTestMocks( TextBuffer = workspace.Documents.Single().GetTextBuffer(); } - internal LanguageServiceBrokerCallback LanguageServiceBrokerCallback { get; } + internal LanguageServiceBrokerCallback LanguageServiceBrokerCallback { get; } internal IThreadingContext ThreadingContext { get; } @@ -84,27 +79,26 @@ protected async Task CreateMocksAsync(string code) var workspace = EditorTestWorkspace.CreateCSharp(code, composition: s_composition); var threadingContext = workspace.GetService(); - var clientCapabilities = new LSP.ClientCapabilities() + var testLspServer = await CreateTestLspServerAsync(workspace, new InitializationOptions { - TextDocument = new LSP.TextDocumentClientCapabilities() - { - DocumentSymbol = new LSP.DocumentSymbolSetting() - { - HierarchicalDocumentSymbolSupport = true - } - } - }; - - var testLspServer = await CreateTestLspServerAsync(workspace, new InitializationOptions { ClientCapabilities = clientCapabilities }); + // Set the message formatter to use newtonsoft on the client side to match real behavior. + // Also avoid calling initialize / initialized as the test harness uses types only compatible with STJ. + // TODO - switch back to STJ with https://github.com/dotnet/roslyn/issues/73317 + ClientMessageFormatter = new JsonMessageFormatter(), + CallInitialize = false, + CallInitialized = false + }); var mocks = new DocumentOutlineTestMocks(RequestAsync, threadingContext, workspace, testLspServer); return mocks; - async Task RequestAsync(ITextBuffer textBuffer, Func capabilitiesFilter, string languageServerName, string method, Func parameterFactory, CancellationToken cancellationToken) + async Task RequestAsync(Request request, CancellationToken cancellationToken) { - var request = parameterFactory(textBuffer.CurrentSnapshot).ToObject(); - var response = await testLspServer.ExecuteRequestAsync(method, request!, cancellationToken); - return new ManualInvocationResponse(string.Empty, JToken.FromObject(response!)); + var docRequest = (DocumentRequest)request; + var parameters = docRequest.ParameterFactory(docRequest.TextBuffer.CurrentSnapshot); + var response = await testLspServer.ExecuteRequestAsync(request.Method, parameters, cancellationToken); + + return response; } } @@ -139,7 +133,21 @@ private async Task CreateTestLspServerAsync(EditorTestWorkspace w var workspaceWaiter = operations.GetWaiter(FeatureAttribute.Workspace); await workspaceWaiter.ExpeditedWaitAsync(); - return await TestLspServer.CreateAsync(workspace, initializationOptions, _logger); + var server = await TestLspServer.CreateAsync(workspace, initializationOptions, _logger); + + // We disable the default test initialize call because the default test harness intialize types only support STJ (not newtonsoft). + // We only care that initialize has been called with some capability, so call with simple objects. + // TODO - remove with switch to STJ in https://github.com/dotnet/roslyn/issues/73317 + await server.ExecuteRequestAsync(Roslyn.LanguageServer.Protocol.Methods.InitializeName, new NewtonsoftInitializeParams() { Capabilities = new object() }, CancellationToken.None); + + return server; + } + + [DataContract] + private class NewtonsoftInitializeParams + { + [DataMember(Name = "capabilities")] + internal object? Capabilities { get; set; } } } } diff --git a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs index b695530f678da..3f422f9f713b9 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Progress; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -218,7 +219,7 @@ private static async Task FixProjectsAsync( progressTracker.AddItems(projects.Sum(static p => p.DocumentIds.Count)); // Run in parallel across all projects. - return await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: projects, produceItems: static async (project, callback, args, cancellationToken) => { @@ -245,17 +246,10 @@ await RoslynParallel.ForEachAsync( callback((document.Id, await fixedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); }).ConfigureAwait(false); }, - consumeItems: static async (stream, args, cancellationToken) => - { - // Now consume the changed documents, applying their new roots to the solution. - var currentSolution = args.solution; - await foreach (var (documentId, newRoot) in stream) - currentSolution = currentSolution.WithDocumentSyntaxRoot(documentId, newRoot); - - return currentSolution; - }, args: (globalOptions, solution, enabledFixIds, progressTracker), cancellationToken).ConfigureAwait(false); + + return solution.WithDocumentSyntaxRoots(changedRoots); } private static async Task FixDocumentAsync( diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs index e9143db12bc2c..5be44e0a29717 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs index 7b0668f518478..4d632cf85c644 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -12,6 +13,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.Text; using Newtonsoft.Json.Linq; @@ -23,8 +25,7 @@ namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline; using LspDocumentSymbol = DocumentSymbol; using Range = Roslyn.LanguageServer.Protocol.Range; -internal delegate Task LanguageServiceBrokerCallback( - ITextBuffer textBuffer, Func capabilitiesFilter, string languageServerName, string method, Func parameterFactory, CancellationToken cancellationToken); +internal delegate Task LanguageServiceBrokerCallback(Request request, CancellationToken cancellationToken); internal sealed partial class DocumentOutlineViewModel { @@ -32,34 +33,29 @@ internal sealed partial class DocumentOutlineViewModel /// Makes an LSP document symbol request and returns the response and the text snapshot used at /// the time the LSP client sends the request to the server. /// - public static async Task<(JToken response, ITextSnapshot snapshot)?> DocumentSymbolsRequestAsync( + public static async Task<(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol[] response, ITextSnapshot snapshot)?> DocumentSymbolsRequestAsync( ITextBuffer textBuffer, - LanguageServiceBrokerCallback callbackAsync, + LanguageServiceBrokerCallback callbackAsync, string textViewFilePath, CancellationToken cancellationToken) { ITextSnapshot? requestSnapshot = null; - JToken ParameterFunction(ITextSnapshot snapshot) + + var request = new DocumentRequest() { - requestSnapshot = snapshot; - return JToken.FromObject(new RoslynDocumentSymbolParams() + Method = Methods.TextDocumentDocumentSymbolName, + LanguageServerName = WellKnownLspServerKinds.AlwaysActiveVSLspServer.ToUserVisibleString(), + TextBuffer = textBuffer, + ParameterFactory = (snapshot) => { - UseHierarchicalSymbols = true, - TextDocument = new TextDocumentIdentifier() - { - Uri = ProtocolConversions.CreateAbsoluteUri(textViewFilePath) - } - }); - } + requestSnapshot = snapshot; + return new DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbolParams( + new DocumentSymbolNewtonsoft.NewtonsoftTextDocumentIdentifier(ProtocolConversions.CreateAbsoluteUri(textViewFilePath)), + UseHierarchicalSymbols: true); + } + }; - var manualResponse = await callbackAsync( - textBuffer: textBuffer, - method: Methods.TextDocumentDocumentSymbolName, - capabilitiesFilter: _ => true, - languageServerName: WellKnownLspServerKinds.AlwaysActiveVSLspServer.ToUserVisibleString(), - parameterFactory: ParameterFunction, - cancellationToken: cancellationToken).ConfigureAwait(false); - var response = manualResponse?.Response; + var response = await callbackAsync(request, cancellationToken).ConfigureAwait(false); // The request snapshot or response can be null if there is no LSP server implementation for // the document symbol request for that language. @@ -100,12 +96,8 @@ JToken ParameterFunction(ITextSnapshot snapshot) /// ] /// } /// ] - public static ImmutableArray CreateDocumentSymbolData(JToken token, ITextSnapshot textSnapshot) + public static ImmutableArray CreateDocumentSymbolData(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol[] documentSymbols, ITextSnapshot textSnapshot) { - // If we get no value results back, treat that as empty results. That way we don't keep showing stale - // results if the server starts returning nothing. - var documentSymbols = token.ToObject() ?? []; - // Obtain a flat list of all the document symbols sorted by location in the document. var allSymbols = documentSymbols .SelectMany(x => x.Children) @@ -124,7 +116,7 @@ public static ImmutableArray CreateDocumentSymbolData(JToken // Returns the symbol in the list at index start (the parent symbol) with the following symbols in the list // (descendants) appropriately nested into the parent. - DocumentSymbolData NestDescendantSymbols(ImmutableArray allSymbols, int start, out int newStart) + DocumentSymbolData NestDescendantSymbols(ImmutableArray allSymbols, int start, out int newStart) { var currentParent = allSymbols[start]; start++; @@ -149,7 +141,7 @@ DocumentSymbolData NestDescendantSymbols(ImmutableArray al // Return the nested parent symbol. return new DocumentSymbolData( currentParent.Detail ?? currentParent.Name, - currentParent.Kind, + (Roslyn.LanguageServer.Protocol.SymbolKind)currentParent.Kind, (Glyph)currentParent.Glyph, GetSymbolRangeSpan(currentParent.Range), GetSymbolRangeSpan(currentParent.SelectionRange), @@ -157,15 +149,18 @@ DocumentSymbolData NestDescendantSymbols(ImmutableArray al } // Returns whether the child symbol is in range of the parent symbol. - static bool Contains(LspDocumentSymbol parent, LspDocumentSymbol child) + static bool Contains(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol parent, DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol child) { - var parentRange = ProtocolConversions.RangeToLinePositionSpan(parent.Range); - var childRange = ProtocolConversions.RangeToLinePositionSpan(child.Range); + var parentRange = RangeToLinePositionSpan(parent.Range); + var childRange = RangeToLinePositionSpan(child.Range); return childRange.Start > parentRange.Start && childRange.End <= parentRange.End; + + static LinePositionSpan RangeToLinePositionSpan(DocumentSymbolNewtonsoft.NewtonsoftRange range) + => new(new LinePosition(range.Start.Line, range.Start.Character), new LinePosition(range.End.Line, range.End.Character)); } // Converts a Document Symbol Range to a SnapshotSpan within the text snapshot used for the LSP request. - SnapshotSpan GetSymbolRangeSpan(Range symbolRange) + SnapshotSpan GetSymbolRangeSpan(DocumentSymbolNewtonsoft.NewtonsoftRange symbolRange) { var originalStartPosition = textSnapshot.GetLineFromLineNumber(symbolRange.Start.Line).Start.Position + symbolRange.Start.Character; var originalEndPosition = textSnapshot.GetLineFromLineNumber(symbolRange.End.Line).Start.Position + symbolRange.End.Character; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs new file mode 100644 index 0000000000000..962a451c2c5a5 --- /dev/null +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs @@ -0,0 +1,127 @@ +// 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.Globalization; +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline; + +/// +/// These are very temporary types that we need in order to serialize document symbol data +/// using Newtonsoft instead of System.Text.Json +/// +/// We currently must support Newtonsoft serialization here because we have not yet opted into using STJ +/// in the VS language server client (and so the client will serialize the request using Newtonsoft). +/// +/// https://github.com/dotnet/roslyn/pull/72675 tracks opting in the client to STJ. +/// TODO - everything in this type should be deleted once the client side is using STJ. +/// +internal class DocumentSymbolNewtonsoft +{ + private class NewtonsoftDocumentUriConverter : JsonConverter + { + /// + public override bool CanConvert(Type objectType) + { + return true; + } + + /// + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + reader = reader ?? throw new ArgumentNullException(nameof(reader)); + if (reader.TokenType == JsonToken.String) + { + var token = JToken.ReadFrom(reader); + var uri = new Uri(token.ToObject()); + + return uri; + } + else if (reader.TokenType == JsonToken.Null) + { + return null; + } + + throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.DocumentUriSerializationError, reader.Value)); + } + + /// + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + writer = writer ?? throw new ArgumentNullException(nameof(writer)); + + if (value is Uri uri) + { + var token = JToken.FromObject(uri.AbsoluteUri); + token.WriteTo(writer); + } + else + { + throw new ArgumentException($"{nameof(value)} must be of type {nameof(Uri)}"); + } + } + } + + [DataContract] + internal record NewtonsoftTextDocumentIdentifier([property: DataMember(Name = "uri"), JsonConverter(typeof(NewtonsoftDocumentUriConverter))] Uri Uri); + + [DataContract] + internal record NewtonsoftRoslynDocumentSymbolParams( + [property: DataMember(Name = "textDocument")] NewtonsoftTextDocumentIdentifier TextDocument, + [property: DataMember(Name = "useHierarchicalSymbols"), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] bool UseHierarchicalSymbols); + + [DataContract] + internal record NewtonsoftRoslynDocumentSymbol( + [property: DataMember(IsRequired = true, Name = "name")] string Name, + [property: DataMember(Name = "detail")][property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Detail, + [property: DataMember(Name = "kind")] NewtonsoftSymbolKind Kind, + [property: DataMember(Name = "deprecated")][property: JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] bool Deprecated, + [property: DataMember(IsRequired = true, Name = "range")] NewtonsoftRange Range, + [property: DataMember(IsRequired = true, Name = "selectionRange")] NewtonsoftRange SelectionRange, + [property: DataMember(Name = "children")][property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] NewtonsoftRoslynDocumentSymbol[]? Children, + [property: DataMember(Name = "glyph")] int Glyph); + + [DataContract] + internal record NewtonsoftRange( + [property: DataMember(Name = "start"), JsonProperty(Required = Required.Always)] NewtonsoftPosition Start, + [property: DataMember(Name = "end"), JsonProperty(Required = Required.Always)] NewtonsoftPosition End); + + [DataContract] + internal record NewtonsoftPosition([property: DataMember(Name = "line")] int Line, [property: DataMember(Name = "character")] int Character); + + [DataContract] + internal enum NewtonsoftSymbolKind + { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, + } +} diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index e097e22eeee74..180174c688ae6 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; @@ -66,12 +67,13 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // Since we know we are on the UI thread, lets get the base indentation now, so that there is less // cleanup work to do later in Venus. var ruleFactory = Workspace.Services.GetService(); - var rules = ruleFactory.CreateRule(documentSyntax, start).Concat(Formatter.GetDefaultFormattingRules(document.Project.Services)); // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); - var originalChanges = formatter.GetFormattingResult(root, [adjustedSpan], formattingOptions, rules, cancellationToken) - .GetTextChanges(cancellationToken); + var originalChanges = formatter.GetFormattingResult( + root, [adjustedSpan], formattingOptions, + [ruleFactory.CreateRule(documentSyntax, start), .. Formatter.GetDefaultFormattingRules(document.Project.Services)], + cancellationToken).GetTextChanges(cancellationToken); var originalSpan = RoslynTextSpan.FromBounds(start, end); var formattedChanges = ruleFactory.FilterFormattedChanges(document.Id, originalSpan, originalChanges); diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs index 545a528028c67..17a4c3c03891b 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Text; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor; @@ -21,6 +22,7 @@ using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using IServiceProvider = System.IServiceProvider; using Task = System.Threading.Tasks.Task; @@ -519,11 +521,9 @@ private static async Task FindReferencesAsync( try { - // Kick off the work to do the actual finding on a BG thread. That way we don' - // t block the calling (UI) thread too long if we happen to do our work on this - // thread. - await Task.Run( - () => FindReferencesAsync(symbolListItem, project, context, classificationOptions, cancellationToken), cancellationToken).ConfigureAwait(false); + // Switch to teh background so we don't block the calling thread (the UI thread) while we're doing this work. + await TaskScheduler.Default; + await FindReferencesAsync(symbolListItem, project, context, classificationOptions, cancellationToken).ConfigureAwait(false); } finally { diff --git a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs index c0d8b86af61c9..6051c5d55f959 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.VisualStudio.Search.Data; +using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.NavigateTo; @@ -46,7 +47,7 @@ public override async Task PerformSearchAsync(ISearchQuery searchQuery, ISearchC var cancellationTriggeredTask = Task.Delay(-1, cancellationToken); // Now, kick off the actual search work concurrently with the waiting task. - var searchTask = Task.Run(() => PerformSearchWorkerAsync(searchQuery, searchCallback, cancellationToken), cancellationToken); + var searchTask = PerformSearchWorkerAsync(searchQuery, searchCallback, cancellationToken); // Now wait for either task to complete. This allows us to bail out of the call into us once the // cancellation token is signaled, even if search work is still happening. This is desirable as the @@ -65,6 +66,9 @@ private async Task PerformSearchWorkerAsync( ISearchCallback searchCallback, CancellationToken cancellationToken) { + // Ensure we yield immediately so our caller can proceed with other work. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + var searchValue = searchQuery.QueryString.Trim(); if (string.IsNullOrWhiteSpace(searchValue)) return; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 5d5366d40dd6b..f6c0795f1e344 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -124,7 +124,7 @@ public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServicePro ProjectSystemProjectFactory = new ProjectSystemProjectFactory(this, FileChangeWatcher, CheckForAddedFileBeingOpenMaybeAsync, RemoveProjectFromMaps); - _ = Task.Run(() => InitializeUIAffinitizedServicesAsync(asyncServiceProvider)); + InitializeUIAffinitizedServicesAsync(asyncServiceProvider).Forget(); _lazyExternalErrorDiagnosticUpdateSource = new Lazy(() => new ExternalErrorDiagnosticUpdateSource( @@ -183,8 +183,9 @@ internal void SubscribeExternalErrorDiagnosticUpdateSourceToSolutionBuildEvents( public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asyncServiceProvider) { + // Yield the thread, so the caller can proceed and return immediately. // Create services that are bound to the UI thread - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _threadingContext.DisposalToken); // Fetch the session synchronously on the UI thread; if this doesn't happen before we try using this on // the background thread then we will experience hangs like we see in this bug: diff --git a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs index a658f1d2e748b..b29accb7d3325 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.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. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,8 +22,8 @@ using Microsoft.VisualStudio.LanguageServices.Storage; using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Settings; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using VSShell = Microsoft.VisualStudio.Shell; @@ -40,18 +37,18 @@ namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch; /// date by downloading patches on a daily basis. /// [ExportWorkspaceService(typeof(ISymbolSearchService), ServiceLayer.Host), Shared] -internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedService, ISymbolSearchService +internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedService, ISymbolSearchService, IDisposable { private readonly SemaphoreSlim _gate = new(initialCount: 1); // Note: A remote engine is disposable as it maintains a connection with ServiceHub, // but we want to keep it alive until the VS is closed, so we don't dispose it. - private ISymbolSearchUpdateEngine _lazyUpdateEngine; + private ISymbolSearchUpdateEngine? _lazyUpdateEngine; private readonly SVsServiceProvider _serviceProvider; private readonly IPackageInstallerService _installerService; - private string _localSettingsDirectory; + private string? _localSettingsDirectory; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -69,7 +66,32 @@ public VisualStudioSymbolSearchService( [SymbolSearchOptionsStorage.SearchReferenceAssemblies, SymbolSearchOptionsStorage.SearchNuGetPackages]) { _serviceProvider = serviceProvider; - _installerService = workspace.Services.GetService(); + _installerService = workspace.Services.GetRequiredService(); + } + + public void Dispose() + { + // Once we're disposed, swap out our engine with a no-op one so we don't try to do any more work, and dispose of + // our connection to the OOP server so it can be cleaned up. + // + // Kick off a Task for this so we don't block MEF from proceeding (as it will be calling us on the UI thread). + _ = DisposeAsync(); + return; + + async Task DisposeAsync() + { + // Make sure we get off the UI thread so that Dispose can return immediately. + await TaskScheduler.Default; + + ISymbolSearchUpdateEngine? updateEngine; + using (await _gate.DisposableWaitAsync().ConfigureAwait(false)) + { + updateEngine = _lazyUpdateEngine; + _lazyUpdateEngine = SymbolSearchUpdateNoOpEngine.Instance; + } + + updateEngine?.Dispose(); + } } protected override async Task EnableServiceAsync(CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs index 711cf06437e77..042a8bb5877a1 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs @@ -28,6 +28,7 @@ internal sealed class AggregatingTelemetryLog : ITelemetryLog private readonly HistogramConfiguration? _histogramConfiguration; private readonly string _eventName; private readonly FunctionId _functionId; + private readonly AggregatingTelemetryLogManager _aggregatingTelemetryLogManager; private readonly object _flushLock; private ImmutableDictionary Histogram, TelemetryEvent TelemetryEvent, object Lock)> _histograms = ImmutableDictionary, TelemetryEvent, object)>.Empty; @@ -39,7 +40,7 @@ internal sealed class AggregatingTelemetryLog : ITelemetryLog /// Used to derive meter name /// Optional values indicating bucket boundaries in milliseconds. If not specified, /// all histograms created will use the default histogram configuration - public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, double[]? bucketBoundaries) + public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, double[]? bucketBoundaries, AggregatingTelemetryLogManager aggregatingTelemetryLogManager) { var meterName = TelemetryLogger.GetPropertyName(functionId, "meter"); var meterProvider = new VSTelemetryMeterProvider(); @@ -48,6 +49,7 @@ public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, _meter = meterProvider.CreateMeter(meterName, version: MeterVersion); _eventName = TelemetryLogger.GetEventName(functionId); _functionId = functionId; + _aggregatingTelemetryLogManager = aggregatingTelemetryLogManager; _flushLock = new(); if (bucketBoundaries != null) @@ -102,6 +104,8 @@ public void Log(KeyValueLogMessage logMessage) { histogram.Record(value); } + + _aggregatingTelemetryLogManager.EnsureTelemetryWorkQueued(); } public IDisposable? LogBlockTime(KeyValueLogMessage logMessage, int minThresholdMs) diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs index 2b3042e607592..1188da45610ee 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs @@ -2,24 +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. +using System; using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Telemetry; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Telemetry; /// -/// Manages creation and obtaining aggregated telemetry logs. +/// Manages creation and obtaining aggregated telemetry logs. Also, notifies logs to +/// send aggregated events every 30 minutes. /// internal sealed class AggregatingTelemetryLogManager { + private static readonly TimeSpan s_batchedTelemetryCollectionPeriod = TimeSpan.FromMinutes(30); + private readonly TelemetrySession _session; + private readonly AsyncBatchingWorkQueue _postTelemetryQueue; private ImmutableDictionary _aggregatingLogs = ImmutableDictionary.Empty; - public AggregatingTelemetryLogManager(TelemetrySession session) + public AggregatingTelemetryLogManager(TelemetrySession session, IAsynchronousOperationListener asyncListener) { _session = session; + + _postTelemetryQueue = new AsyncBatchingWorkQueue( + s_batchedTelemetryCollectionPeriod, + PostCollectedTelemetryAsync, + asyncListener, + CancellationToken.None); } public ITelemetryLog? GetLog(FunctionId functionId, double[]? bucketBoundaries) @@ -27,11 +42,22 @@ public AggregatingTelemetryLogManager(TelemetrySession session) if (!_session.IsOptedIn) return null; - return ImmutableInterlocked.GetOrAdd( - ref _aggregatingLogs, - functionId, - static (functionId, arg) => new AggregatingTelemetryLog(arg._session, functionId, arg.bucketBoundaries), - factoryArgument: (_session, bucketBoundaries)); + return ImmutableInterlocked.GetOrAdd(ref _aggregatingLogs, functionId, functionId => new AggregatingTelemetryLog(_session, functionId, bucketBoundaries, this)); + } + + public void EnsureTelemetryWorkQueued() + { + // Ensure PostCollectedTelemetryAsync will get fired after the collection period. + _postTelemetryQueue.AddWork(); + } + + private ValueTask PostCollectedTelemetryAsync(CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + Flush(); + + return ValueTaskFactory.CompletedTask; } public void Flush() diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs index 16e44077e4178..3bc330135068c 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs @@ -17,17 +17,17 @@ internal sealed class TelemetryLogProvider : ITelemetryLogProvider private readonly AggregatingTelemetryLogManager _aggregatingTelemetryLogManager; private readonly VisualStudioTelemetryLogManager _visualStudioTelemetryLogManager; - private TelemetryLogProvider(TelemetrySession session, ILogger telemetryLogger) + private TelemetryLogProvider(TelemetrySession session, ILogger telemetryLogger, IAsynchronousOperationListener asyncListener) { - _aggregatingTelemetryLogManager = new AggregatingTelemetryLogManager(session); + _aggregatingTelemetryLogManager = new AggregatingTelemetryLogManager(session, asyncListener); _visualStudioTelemetryLogManager = new VisualStudioTelemetryLogManager(session, telemetryLogger); } public static TelemetryLogProvider Create(TelemetrySession session, ILogger telemetryLogger, IAsynchronousOperationListener asyncListener) { - var logProvider = new TelemetryLogProvider(session, telemetryLogger); + var logProvider = new TelemetryLogProvider(session, telemetryLogger, asyncListener); - TelemetryLogging.SetLogProvider(logProvider, asyncListener); + TelemetryLogging.SetLogProvider(logProvider); return logProvider; } diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs index 114576ac819e8..fe4d67f41a20f 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; @@ -804,13 +805,13 @@ private void AdjustIndentationForSpan( venusFormattingRules.Add(baseIndentationRule); venusFormattingRules.Add(ContainedDocumentPreserveFormattingRule.Instance); - var formattingRules = venusFormattingRules.Concat(Formatter.GetDefaultFormattingRules(document)); - var services = document.Project.Solution.Services; var formatter = document.GetRequiredLanguageService(); var changes = formatter.GetFormattingResult( root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan(root, visibleSpan) }, - options, formattingRules, CancellationToken.None).GetTextChanges(CancellationToken.None); + options, + [.. venusFormattingRules, .. Formatter.GetDefaultFormattingRules(document)], + CancellationToken.None).GetTextChanges(CancellationToken.None); visibleSpans.Add(visibleSpan); var newChanges = FilterTextChanges(document.GetTextSynchronously(CancellationToken.None), visibleSpans, changes.ToReadOnlyCollection()).Where(t => visibleSpan.Contains(t.Span)); diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs index d3aa181cca369..091bb01bbda55 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs @@ -228,14 +228,12 @@ public static Tuple EnsureEventHandler( newRoot = Simplifier.ReduceAsync( targetDocument.WithSyntaxRoot(newRoot), Simplifier.Annotation, options.CleanupOptions.SimplifierOptions, cancellationToken).WaitAndGetResult_Venus(cancellationToken).GetSyntaxRootSynchronously(cancellationToken); - var formattingRules = additionalFormattingRule.Concat(Formatter.GetDefaultFormattingRules(targetDocument)); - newRoot = Formatter.Format( newRoot, Formatter.Annotation, targetDocument.Project.Solution.Services, options.CleanupOptions.FormattingOptions, - formattingRules, + [additionalFormattingRule, .. Formatter.GetDefaultFormattingRules(targetDocument)], cancellationToken); var newMember = newRoot.GetAnnotatedNodesAndTokens(annotation).Single(); diff --git a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs index c412f1813e751..415f396d8f642 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs @@ -7,11 +7,13 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.ExternalElements; using Microsoft.VisualStudio.LanguageServices.Implementation.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel @@ -39,8 +41,8 @@ private RootCodeModel(CodeModelState state, EnvDTE.Project parent, ProjectId pro private Project GetProject() => Workspace.CurrentSolution.GetProject(_projectId); - private Compilation GetCompilation() - => GetProject().GetCompilationAsync().Result; + private Task GetCompilationAsync() + => GetProject().GetCompilationAsync(); private ComHandle GetFileCodeModel(object location) { @@ -127,27 +129,34 @@ EnvDTE.CodeElements ICodeElementContainer.GetCollec => CodeElements; public EnvDTE.CodeElements CodeElements - { - get + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - var compilation = GetCompilation(); + var compilation = await GetCompilationAsync().ConfigureAwait(true); + + // Need to ensure we're on the UI thread to make the ExternalCodeNamespace. It creates UI thread bound + // com aggregates. + await this.State.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); var rootNamespace = ExternalCodeNamespace.Create(this.State, _projectId, compilation.GlobalNamespace); return rootNamespace.Members; - } - } + }); public EnvDTE.CodeType CodeTypeFromFullName(string name) - { - var compilation = GetCompilation(); - var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(name, compilation); - if (typeSymbol == null || - typeSymbol.TypeKind is TypeKind.Error or TypeKind.Unknown) + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - return null; - } + var compilation = await GetCompilationAsync().ConfigureAwait(true); + var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(name, compilation); + if (typeSymbol == null || + typeSymbol.TypeKind is TypeKind.Error or TypeKind.Unknown) + { + return null; + } - return (EnvDTE.CodeType)CodeModelService.CreateCodeType(this.State, _projectId, typeSymbol); - } + // Need to ensure we're on the UI thread to make the call to CreateCodeType. It creates UI thread + // bound com aggregates. + await this.State.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return (EnvDTE.CodeType)CodeModelService.CreateCodeType(this.State, _projectId, typeSymbol); + }); public EnvDTE.CodeTypeRef CreateCodeTypeRef(object type) => CodeModelService.CreateCodeTypeRef(this.State, _projectId, type); @@ -159,16 +168,15 @@ public void Remove(object element) => throw Exceptions.ThrowENotImpl(); public string DotNetNameFromLanguageSpecific(string languageName) - { - var compilation = GetCompilation(); - var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(languageName, compilation); - if (typeSymbol == null) + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - throw Exceptions.ThrowEInvalidArg(); - } + var compilation = await GetCompilationAsync().ConfigureAwait(true); + var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(languageName, compilation); + if (typeSymbol == null) + throw Exceptions.ThrowEInvalidArg(); - return MetadataNameHelpers.GetMetadataName(typeSymbol); - } + return MetadataNameHelpers.GetMetadataName(typeSymbol); + }); public EnvDTE.CodeElement ElementFromID(string id) => throw Exceptions.ThrowENotImpl(); diff --git a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs index d638391df2977..c42102f1d18db 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs @@ -704,7 +704,7 @@ private static AnalyzerFileReference CreateShadowCopiedAnalyzerReference(TempRoo return new AnalyzerFileReference(original, new MockShadowCopyAnalyzerAssemblyLoader(ImmutableDictionary.Empty.Add(original, shadow.Path))); } - private class MissingAnalyzerLoader : AnalyzerAssemblyLoader + private class MissingAnalyzerLoader() : AnalyzerAssemblyLoader([]) { protected override string PreparePathToLoad(string fullPath) => throw new FileNotFoundException(fullPath); diff --git a/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj b/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj index 339b5d2a742e6..396ab0ff2e66d 100644 --- a/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj +++ b/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj @@ -9,8 +9,7 @@ .NET Compiler Platform ("Roslyn") Language Server Protocol internal. true - CollectPackInputs;$(BeforePack) - + $(NoWarn);NU5100 @@ -45,18 +44,20 @@ - - - + + + + + + + + + + + + - - - - - - + <_Content Include="@(Content)" Condition="'%(Content.Pack)'=='true'"/> diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index d739438f4952f..6b93eb53390b8 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -167,17 +167,6 @@ TargetFramework=net472 BindingRedirect - - Workspaces.MSBuild - BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup - true - TargetFramework=net472 - BindingRedirect - - false - CSharpWorkspace BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup @@ -318,11 +307,8 @@ SemanticSearchRefs - - TargetFramework=$(NetRoslyn) true false - false SemanticSearchRefs PublishProjectOutputGroup diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs index 5df82749fae48..61015f6f2af0a 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs @@ -331,10 +331,8 @@ public ImmutableArray GetFormattingChangesOnPaste(ParsedDocument doc var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(document.Root, textSpan); var service = _services.GetRequiredService(); - var rules = new List() { new PasteFormattingRule() }; - rules.AddRange(service.GetDefaultFormattingRules()); - - var result = service.GetFormattingResult(document.Root, [formattingSpan], options, rules, cancellationToken); + var result = service.GetFormattingResult( + document.Root, [formattingSpan], options, [new PasteFormattingRule(), .. service.GetDefaultFormattingRules()], cancellationToken); return [.. result.GetTextChanges(cancellationToken)]; } diff --git a/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs index 96365c7cbe9e6..5cca530503ddf 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -13,7 +14,7 @@ internal class TypingFormattingRule : BaseFormattingRule { public static readonly TypingFormattingRule Instance = new(); - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { if (TryAddSuppressionOnMissingCloseBraceCase(list, node)) { @@ -23,7 +24,7 @@ public override void AddSuppressOperations(List list, SyntaxN base.AddSuppressOperations(list, node, in nextOperation); } - private static bool TryAddSuppressionOnMissingCloseBraceCase(List list, SyntaxNode node) + private static bool TryAddSuppressionOnMissingCloseBraceCase(ArrayBuilder list, SyntaxNode node) { var bracePair = node.GetBracePair(); if (!bracePair.IsValidBracketOrBracePair()) diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index f3075ecfe3cea..7b46d9a52c515 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -10,18 +10,21 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification; [ExportLanguageService(typeof(ISimplificationService), LanguageNames.CSharp), Shared] -internal partial class CSharpSimplificationService : AbstractSimplificationService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class CSharpSimplificationService() + : AbstractSimplificationService(s_reducers) { // 1. the cast simplifier should run earlier then everything else to minimize the type expressions // 2. Extension method reducer may insert parentheses. So run it before the parentheses remover. @@ -40,12 +43,6 @@ internal partial class CSharpSimplificationService : AbstractSimplificationServi new CSharpDefaultExpressionReducer(), ]; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpSimplificationService() : base(s_reducers) - { - } - public override SimplifierOptions DefaultOptions => CSharpSimplifierOptions.Default; @@ -230,4 +227,26 @@ private static bool IsTupleInDeconstruction(SyntaxNode tuple) } while (true); } + + protected override void AddImportDeclarations(CompilationUnitSyntax root, ArrayBuilder importDeclarations) + { + importDeclarations.AddRange(root.Usings); + + foreach (var member in root.Members) + { + if (member is BaseNamespaceDeclarationSyntax baseNamespace) + AddImportDeclarations(baseNamespace, importDeclarations); + } + + static void AddImportDeclarations(BaseNamespaceDeclarationSyntax baseNamespace, ArrayBuilder importDeclarations) + { + importDeclarations.AddRange(baseNamespace.Usings); + + foreach (var member in baseNamespace.Members) + { + if (member is BaseNamespaceDeclarationSyntax childNamespace) + AddImportDeclarations(childNamespace, importDeclarations); + } + } + } } diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index 2d002177327b1..814853036893e 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -5,6 +5,8 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp; @@ -25,7 +27,13 @@ private sealed class ParsedSyntaxTree : CSharpSyntaxTree private SourceText? _lazyText; - public ParsedSyntaxTree(SourceText? lazyText, CSharpSyntaxNode root, CSharpParseOptions options, string filePath, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm) + public ParsedSyntaxTree( + SourceText? lazyText, + CSharpSyntaxNode root, + CSharpParseOptions options, + string filePath, + Encoding? encoding, + SourceHashAlgorithm checksumAlgorithm) { _lazyText = lazyText; _root = CloneNodeAsRoot(root); @@ -68,10 +76,20 @@ public override bool TryGetRoot([NotNullWhen(true)] out CSharpSyntaxNode? root) } 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); + => 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); + => 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/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs b/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs index 0cba06c47234c..05ad5789c365a 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs @@ -11,11 +11,10 @@ using System.Threading.Tasks; using System.Xml; using Microsoft.Build.Framework; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.MSBuild.Build +namespace Microsoft.CodeAnalysis.MSBuild { internal class ProjectBuildManager { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs b/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs index 4bfd90f760c37..bc1e0320e7e40 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs @@ -4,7 +4,6 @@ extern alias workspaces; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -13,26 +12,22 @@ using Microsoft.Build.Construction; using Microsoft.Build.Locator; using Microsoft.Build.Logging; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.Extensions.Logging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; -internal sealed class BuildHost +internal sealed class BuildHost : IBuildHost { - private readonly ILogger _logger; + private readonly BuildHostLogger _logger; private readonly ImmutableDictionary _globalMSBuildProperties; private readonly string? _binaryLogPath; private readonly RpcServer _server; private readonly object _gate = new object(); private ProjectBuildManager? _buildManager; - public BuildHost(ILoggerFactory loggerFactory, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) + public BuildHost(BuildHostLogger logger, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) { - _logger = loggerFactory.CreateLogger(); + _logger = logger; _globalMSBuildProperties = globalMSBuildProperties; _binaryLogPath = binaryLogPath; _server = server; @@ -109,7 +104,7 @@ private bool TryEnsureMSBuildLoaded(string projectOrSolutionFilePath) #if NET472 || NET6_0 // If we're compiling against net472 or net6.0, we get our MemberNotNull from the workspaces assembly. It has it in the net6.0 case since we're consuming the netstandard2.0 version of Workspaces. [workspaces::System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))] #else // If we're compiling against net7.0 or higher, then we're getting it staright from the framework. - [MemberNotNull(nameof(_buildManager))] + [System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))] #endif [MethodImpl(MethodImplOptions.NoInlining)] // Do not inline this, since this creates MSBuild types which are being loaded by the caller private void CreateBuildManager() @@ -177,8 +172,8 @@ public async Task LoadProjectFileAsync(string projectFilePath, string langu ProjectFileLoader projectLoader = languageName switch { - LanguageNames.CSharp => new CSharp.CSharpProjectFileLoader(), - LanguageNames.VisualBasic => new VisualBasic.VisualBasicProjectFileLoader(), + LanguageNames.CSharp => new CSharpProjectFileLoader(), + LanguageNames.VisualBasic => new VisualBasicProjectFileLoader(), _ => throw ExceptionUtilities.UnexpectedValue(languageName) }; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.cs b/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.cs new file mode 100644 index 0000000000000..c58341779c7f8 --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.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. + +using System.IO; + +namespace Microsoft.CodeAnalysis.MSBuild; + +internal sealed class BuildHostLogger(TextWriter output) +{ + public void LogInformation(string message) + => output.WriteLine(message); + + public void LogCritical(string message) + => output.WriteLine(message); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs index 5908aa94455a3..35bd8d2b0a204 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal class CSharpCommandLineArgumentReader : CommandLineArgumentReader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs index 91846935a3ae5..cc81848d8f3e4 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs @@ -5,11 +5,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal class CSharpProjectFile : ProjectFile { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs index b578b9d1b0ae9..9f7400d847110 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs @@ -2,12 +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 Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal partial class CSharpProjectFileLoader : ProjectFileLoader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs index 9d30cf894f6a3..aa616bc5e3f5e 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class DiagnosticLog : IEnumerable { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs index b0a3710cf91b5..b1edc6c883208 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs @@ -4,7 +4,7 @@ using System; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class MSBuildDiagnosticLogItem : DiagnosticLogItem { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs index 0454566bdb2f8..43f720925310d 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs @@ -6,7 +6,7 @@ using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class MSBuildDiagnosticLogger : MSB.Framework.ILogger { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs index 92fb55d25e1b1..788b90134bd08 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs @@ -11,14 +11,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; using MSB = Microsoft.Build; namespace Microsoft.CodeAnalysis.MSBuild { - internal abstract class ProjectFile + internal abstract class ProjectFile : IProjectFile { private readonly ProjectFileLoader _loader; private readonly MSB.Evaluation.Project? _loadedProject; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs index d63ccabff51d7..382d61e014f62 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs @@ -5,8 +5,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs index 4264b0dc46efc..829829239183d 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs @@ -4,11 +4,10 @@ using System; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal class VisualBasicCommandLineArgumentReader : CommandLineArgumentReader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs index d8f195f136e76..2acfd35e517e1 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs @@ -4,12 +4,9 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal class VisualBasicProjectFile : ProjectFile { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs index 99f82fa28e1e6..0ccc774e167f4 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs @@ -2,12 +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 Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal partial class VisualBasicProjectFileLoader : ProjectFileLoader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index 063d82e3b829a..c895b665da4dc 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -28,9 +28,7 @@ - - @@ -41,8 +39,8 @@ - + diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Program.cs b/src/Workspaces/Core/MSBuild.BuildHost/Program.cs index 973f7d5151535..18b67f5d7e705 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Program.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Program.cs @@ -6,11 +6,9 @@ using System.Collections.Immutable; using System.CommandLine; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.Extensions.Logging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class Program { @@ -31,26 +29,13 @@ internal static async Task Main(string[] args) propertiesBuilder.Add(propertyParts[0], propertyParts[1]); } - // Create a console logger that logs everything to standard error instead of standard out; by setting the threshold to Trace - // everything will go to standard error. - var loggerFactory = LoggerFactory.Create(builder => - builder.AddConsole(configure => - { - // DisableColors is deprecated in favor of us moving to simple console, but that loses the LogToStandardErrorThreshold - // which we also need -#pragma warning disable CS0618 - configure.DisableColors = true; -#pragma warning restore CS0618 - configure.LogToStandardErrorThreshold = LogLevel.Trace; - })); - - var logger = loggerFactory.CreateLogger(typeof(Program)); + var logger = new BuildHostLogger(Console.Error); logger.LogInformation($"BuildHost Runtime Version: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"); var server = new RpcServer(sendingStream: Console.OpenStandardOutput(), receivingStream: Console.OpenStandardInput()); - var targetObject = server.AddTarget(new BuildHost(loggerFactory, propertiesBuilder.ToImmutable(), binaryLogPath, server)); + var targetObject = server.AddTarget(new BuildHost(logger, propertiesBuilder.ToImmutable(), binaryLogPath, server)); Contract.ThrowIfFalse(targetObject == 0, "The first object registered should have target 0, which is assumed by the client."); await server.RunAsync().ConfigureAwait(false); diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs similarity index 95% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs index 5be36343b592d..7b9d8897001f5 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { [DataContract] internal class DiagnosticLogItem diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/DocumentFileInfo.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DocumentFileInfo.cs similarity index 100% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/DocumentFileInfo.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DocumentFileInfo.cs diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.cs new file mode 100644 index 0000000000000..dcc0e48ef2f5d --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.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 System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.MSBuild; + +/// +/// RPC methods. +/// +internal interface IBuildHost +{ + bool HasUsableMSBuild(string projectOrSolutionFilePath); + ImmutableArray<(string ProjectPath, string ProjectGuid)> GetProjectsInSolution(string solutionFilePath); + Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken); + Task TryGetProjectOutputPathAsync(string projectFilePath, CancellationToken cancellationToken); + Task ShutdownAsync(); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.cs new file mode 100644 index 0000000000000..7d93476b118ef --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.MSBuild; + +/// +/// RPC methods. +/// +internal interface IProjectFile +{ + ImmutableArray GetDiagnosticLogItems(); + string GetDocumentExtension(SourceCodeKind kind); + Task> GetProjectFileInfosAsync(CancellationToken cancellationToken); + void AddDocument(string filePath, string? logicalPath); + void RemoveDocument(string filePath); + void AddMetadataReference(string metadataReferenceIdentity, MetadataReferenceProperties properties, string? hintPath); + void RemoveMetadataReference(string shortAssemblyName, string fullAssemblyName, string filePath); + void AddProjectReference(string projectName, ProjectFileReference reference); + void RemoveProjectReference(string projectName, string projectFilePath); + void AddAnalyzerReference(string fullPath); + void RemoveAnalyzerReference(string fullPath); + void Save(); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs similarity index 96% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs index a4dee42f83fcf..ee75ca9bf1ca5 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class JsonSettings { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs similarity index 98% rename from src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs index e8f11292cb262..99a376391a654 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class MonoMSBuildDiscovery { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs similarity index 74% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs index a337cfded8bfc..f637b25ff8b32 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs @@ -1,9 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics; using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.MSBuild; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs similarity index 99% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs index bd5fbd6371bf6..e5ac67ffd9721 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics; using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs similarity index 94% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs index fb8d3a216e32a..7a27a20ea17c1 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs @@ -28,7 +28,7 @@ internal sealed class ProjectFileReference public ImmutableArray Aliases { get; } /// - /// The value of . + /// The value of "ReferenceOutputAssembly" metadata. /// [DataMember(Order = 2)] public bool ReferenceOutputAssembly { get; } diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs similarity index 94% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs index 0a7d7beae83e9..4b27b8c022e8a 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs @@ -5,7 +5,7 @@ using System.Collections.Immutable; using Newtonsoft.Json.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class Request { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs similarity index 89% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs index 2fa7d6e3efe61..ff8a604b80a02 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class Response { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs similarity index 97% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs index 8778e50b619ea..b476ad5a1a105 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class TextReaderExtensions { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs index 3390c68f5f450..a6a08b8ed3050 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs @@ -13,7 +13,7 @@ using Newtonsoft.Json.Linq; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; /// /// Implements the server side of the RPC channel used to communicate with the build host. diff --git a/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs b/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs index 0bdbcd428bba0..928ff407b8b67 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs @@ -14,8 +14,6 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; using Microsoft.Extensions.Logging; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs b/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs index b32d95b27ad91..450024049fb33 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 19c234bb6c285..8a89561728484 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -14,7 +14,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs index 35e8f99bf2704..92b03542374ce 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.MSBuild.Rpc; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj index 6fb4366c78c83..9b3e73b619771 100644 --- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj +++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj @@ -24,10 +24,7 @@ - - - + @@ -38,7 +35,7 @@ for it. PrivateAssets="all" is needed to prevent this reference from becoming a package reference in the package, as a workaround for https://github.com/NuGet/Home/issues/3891. --> - + true @@ -50,12 +47,7 @@ InternalUtilities\GlobalAssemblyCache.cs - - - - - - + @@ -63,6 +55,12 @@ + + + + + + diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs index 808d2885e646a..5a457f05da7e8 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs @@ -6,9 +6,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteBuildHost { @@ -23,22 +22,22 @@ public RemoteBuildHost(RpcClient client) } public Task HasUsableMSBuildAsync(string projectOrSolutionFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); public Task> GetProjectsInSolutionAsync(string solutionFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync>(BuildHostTargetObject, nameof(BuildHost.GetProjectsInSolution), parameters: [solutionFilePath], cancellationToken); + => _client.InvokeAsync>(BuildHostTargetObject, nameof(IBuildHost.GetProjectsInSolution), parameters: [solutionFilePath], cancellationToken); public async Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken) { - var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); + var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); return new RemoteProjectFile(_client, remoteProjectFileTargetObject); } public Task TryGetProjectOutputPathAsync(string projectFilePath, CancellationToken cancellationToken) - => _client.InvokeNullableAsync(BuildHostTargetObject, nameof(BuildHost.TryGetProjectOutputPathAsync), parameters: [projectFilePath], cancellationToken); + => _client.InvokeNullableAsync(BuildHostTargetObject, nameof(IBuildHost.TryGetProjectOutputPathAsync), parameters: [projectFilePath], cancellationToken); public Task ShutdownAsync(CancellationToken cancellationToken) - => _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.ShutdownAsync), parameters: [], cancellationToken); + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ShutdownAsync), parameters: [], cancellationToken); } diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs index 0e3d803737ceb..61fde83b8a63c 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs @@ -4,7 +4,7 @@ using System; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteInvocationException : Exception { diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs index a2ce680237812..59b91fbb346c2 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs @@ -5,10 +5,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Logging; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteProjectFile { @@ -22,38 +20,38 @@ public RemoteProjectFile(RpcClient client, int remoteProjectFileTargetObject) } public Task> GetDiagnosticLogItemsAsync(CancellationToken cancellationToken) - => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(ProjectFile.GetDiagnosticLogItems), parameters: [], cancellationToken); + => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(IProjectFile.GetDiagnosticLogItems), parameters: [], cancellationToken); public Task GetDocumentExtensionAsync(SourceCodeKind sourceCodeKind, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.GetDocumentExtension), parameters: [sourceCodeKind], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.GetDocumentExtension), parameters: [sourceCodeKind], cancellationToken); public Task> GetProjectFileInfosAsync(CancellationToken cancellationToken) - => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(ProjectFile.GetProjectFileInfosAsync), parameters: [], cancellationToken); + => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(IProjectFile.GetProjectFileInfosAsync), parameters: [], cancellationToken); public Task AddDocumentAsync(string filePath, string? logicalPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddDocument), parameters: [filePath, logicalPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddDocument), parameters: [filePath, logicalPath], cancellationToken); public Task RemoveDocumentAsync(string filePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveDocument), parameters: [filePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveDocument), parameters: [filePath], cancellationToken); public Task AddMetadataReferenceAsync(string metadataReferenceIdentity, MetadataReferenceProperties properties, string? hintPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddMetadataReference), parameters: [metadataReferenceIdentity, properties, hintPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddMetadataReference), parameters: [metadataReferenceIdentity, properties, hintPath], cancellationToken); public Task RemoveMetadataReferenceAsync(string shortAssemblyName, string fullAssemblyName, string filePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveMetadataReference), parameters: [shortAssemblyName, fullAssemblyName, filePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveMetadataReference), parameters: [shortAssemblyName, fullAssemblyName, filePath], cancellationToken); public Task AddProjectReferenceAsync(string projectName, ProjectFileReference projectFileReference, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddProjectReference), parameters: [projectName, projectFileReference], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddProjectReference), parameters: [projectName, projectFileReference], cancellationToken); public Task RemoveProjectReferenceAsync(string projectName, string projectFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveProjectReference), parameters: [projectName, projectFilePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveProjectReference), parameters: [projectName, projectFilePath], cancellationToken); public Task AddAnalyzerReferenceAsync(string fullPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddAnalyzerReference), parameters: [fullPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddAnalyzerReference), parameters: [fullPath], cancellationToken); public Task RemoveAnalyzerReferenceAsync(string fullPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveAnalyzerReference), parameters: [fullPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveAnalyzerReference), parameters: [fullPath], cancellationToken); public Task SaveAsync(CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.Save), parameters: [], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.Save), parameters: [], cancellationToken); } diff --git a/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs b/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs index 76873433c4c38..83d1a7b43c068 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs @@ -13,10 +13,10 @@ using Newtonsoft.Json.Linq; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; /// -/// Implements the client side of the RPC channel used to communicate with the build host, which is using . +/// Implements the client side of the RPC channel used to communicate with the build host, which is using RpcServer. /// /// /// The RPC system implemented here is pretty close to something like JSON-RPC; however since we need the Build Host to be usable in Source Build diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index 28a3af953bf7f..dac6f076718ea 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -8,14 +8,11 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CaseCorrection; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; @@ -32,7 +29,7 @@ namespace Microsoft.CodeAnalysis.CodeActions; /// /// An action produced by a or a . /// -public abstract class CodeAction +public abstract partial class CodeAction { private static readonly Dictionary s_isNonProgressGetChangedSolutionAsyncOverridden = []; private static readonly Dictionary s_isNonProgressComputeOperationsAsyncOverridden = []; @@ -248,7 +245,7 @@ private protected virtual async Task> GetOpe if (operations != null) { - return await this.PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); + return await PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); } return []; @@ -269,7 +266,7 @@ internal async Task> GetPreviewOperationsAsy if (operations != null) { - return await this.PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); + return await PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); } return []; @@ -408,7 +405,7 @@ protected virtual Task GetChangedDocumentAsync(IProgress GetChangedDocumentInternalAsync(CancellationToken cancellation) @@ -420,11 +417,13 @@ internal Task GetChangedDocumentInternalAsync(CancellationToken cancel /// A list of operations. /// A cancellation token. /// A new list of operations with post processing steps applied to any 's. +#pragma warning disable CA1822 // Mark members as static. This is a public API. protected Task> PostProcessAsync(IEnumerable operations, CancellationToken cancellationToken) - => PostProcessAsync(originalSolution: null!, operations, cancellationToken); +#pragma warning restore CA1822 // Mark members as static + => PostProcessAsync(originalSolution: null, operations, cancellationToken); - internal async Task> PostProcessAsync( - Solution originalSolution, IEnumerable operations, CancellationToken cancellationToken) + internal static async Task> PostProcessAsync( + Solution? originalSolution, IEnumerable operations, CancellationToken cancellationToken) { using var result = TemporaryArray.Empty; @@ -432,7 +431,7 @@ internal async Task> PostProcessAsync( { if (op is ApplyChangesOperation ac) { - result.Add(new ApplyChangesOperation(await this.PostProcessChangesAsync(originalSolution, ac.ChangedSolution, cancellationToken).ConfigureAwait(false))); + result.Add(new ApplyChangesOperation(await PostProcessChangesAsync(originalSolution, ac.ChangedSolution, cancellationToken).ConfigureAwait(false))); } else { @@ -448,11 +447,13 @@ internal async Task> PostProcessAsync( /// /// The solution changed by the . /// A cancellation token +#pragma warning disable CA1822 // Mark members as static. This is a public API. protected Task PostProcessChangesAsync(Solution changedSolution, CancellationToken cancellationToken) - => PostProcessChangesAsync(originalSolution: null!, changedSolution, cancellationToken); +#pragma warning restore CA1822 // Mark members as static + => PostProcessChangesAsync(originalSolution: null, changedSolution, cancellationToken); - internal async Task PostProcessChangesAsync( - Solution originalSolution, + private static async Task PostProcessChangesAsync( + Solution? originalSolution, Solution changedSolution, CancellationToken cancellationToken) { @@ -461,38 +462,10 @@ internal async Task PostProcessChangesAsync( // underneath us). But it's the only option we have for the compat case with existing public extension // points. originalSolution ??= changedSolution.Workspace.CurrentSolution; - var solutionChanges = changedSolution.GetChanges(originalSolution); - var processedSolution = changedSolution; - - // process changed projects - foreach (var projectChanges in solutionChanges.GetProjectChanges()) - { - var documentsToProcess = projectChanges.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true).Concat( - projectChanges.GetAddedDocuments()); - - foreach (var documentId in documentsToProcess) - { - var document = processedSolution.GetRequiredDocument(documentId); - var processedDocument = await PostProcessChangesAsync(document, cancellationToken).ConfigureAwait(false); - processedSolution = processedDocument.Project.Solution; - } - } - - // process completely new projects too - foreach (var addedProject in solutionChanges.GetAddedProjects()) - { - var documentsToProcess = addedProject.DocumentIds; - - foreach (var documentId in documentsToProcess) - { - var document = processedSolution.GetRequiredDocument(documentId); - var processedDocument = await PostProcessChangesAsync(document, cancellationToken).ConfigureAwait(false); - processedSolution = processedDocument.Project.Solution; - } - } - - return processedSolution; + var globalOptions = changedSolution.Services.GetService(); + var fallbackOptions = globalOptions?.Provider ?? CodeActionOptions.DefaultProvider; + return await CleanSyntaxAndSemanticsAsync(originalSolution, changedSolution, fallbackOptions, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); } /// @@ -518,25 +491,6 @@ protected virtual async Task PostProcessChangesAsync(Document document return document; } - internal static async Task CleanupDocumentAsync( - Document document, CodeCleanupOptions options, CancellationToken cancellationToken) - { - document = await ImportAdder.AddImportsFromSymbolAnnotationAsync( - document, Simplifier.AddImportsAnnotation, options.AddImportOptions, cancellationToken).ConfigureAwait(false); - - document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, options.SimplifierOptions, cancellationToken).ConfigureAwait(false); - - // format any node with explicit formatter annotation - document = await Formatter.FormatAsync(document, Formatter.Annotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false); - - // format any elastic whitespace - document = await Formatter.FormatAsync(document, SyntaxAnnotation.ElasticAnnotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false); - - document = await CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken).ConfigureAwait(false); - - return document; - } - #region Factories for standard code actions /// diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeActionWithOptions.cs b/src/Workspaces/Core/Portable/CodeActions/CodeActionWithOptions.cs index 5914b7fbd7a7b..0f8c3b5471b5c 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeActionWithOptions.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeActionWithOptions.cs @@ -44,7 +44,7 @@ public abstract class CodeActionWithOptions : CodeAction if (operations != null) { - operations = await this.PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); + operations = await PostProcessAsync(originalSolution, operations, cancellationToken).ConfigureAwait(false); } return operations; diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs new file mode 100644 index 0000000000000..0e1a0cebd1f9f --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.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.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CaseCorrection; +using Microsoft.CodeAnalysis.CodeCleanup; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeActions; + +public abstract partial class CodeAction +{ + /// + /// We do cleanup in N serialized passes. This allows us to process all documents in parallel, while only forking + /// the solution N times *total* (instead of N times *per* document). + /// + private static readonly ImmutableArray>> s_cleanupPasses = + [ + // First, ensure that everything is formatted as the feature asked for. We want to do this prior to doing + // semantic cleanup as the semantic cleanup passes may end up making changes that end up dropping some of + // the formatting/elastic annotations that the feature wanted respected. + static (document, options, cancellationToken) => CleanupSyntaxAsync(document, options, cancellationToken), + // Then add all missing imports to all changed documents. + static (document, options, cancellationToken) => ImportAdder.AddImportsFromSymbolAnnotationAsync(document, Simplifier.AddImportsAnnotation, options.AddImportOptions, cancellationToken), + // Then simplify any expanded constructs. + static (document, options, cancellationToken) => Simplifier.ReduceAsync(document, Simplifier.Annotation, options.SimplifierOptions, cancellationToken), + // The do any necessary case correction for VB files. + static (document, options, cancellationToken) => CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken), + // Finally, after doing the semantic cleanup, do another syntax cleanup pass to ensure that the tree is in a + // good state. The semantic cleanup passes may have introduced new nodes with elastic trivia that have to be + // cleaned. + static (document, options, cancellationToken) => CleanupSyntaxAsync(document, options, cancellationToken), + ]; + + private static async Task CleanupSyntaxAsync(Document document, CodeCleanupOptions options, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(document.SupportsSyntaxTree); + + // format any node with explicit formatter annotation + var document1 = await Formatter.FormatAsync(document, Formatter.Annotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false); + + // format any elastic whitespace + var document2 = await Formatter.FormatAsync(document1, SyntaxAnnotation.ElasticAnnotation, options.FormattingOptions, cancellationToken).ConfigureAwait(false); + return document2; + } + + internal static ImmutableArray GetAllChangedOrAddedDocumentIds( + Solution originalSolution, + Solution changedSolution) + { + var solutionChanges = changedSolution.GetChanges(originalSolution); + var documentIds = solutionChanges + .GetProjectChanges() + .SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true).Concat(p.GetAddedDocuments())) + .Concat(solutionChanges.GetAddedProjects().SelectMany(p => p.DocumentIds)) + .ToImmutableArray(); + return documentIds; + } + + internal static async Task CleanSyntaxAndSemanticsAsync( + Solution originalSolution, + Solution changedSolution, + CodeCleanupOptionsProvider optionsProvider, + IProgress progress, + CancellationToken cancellationToken) + { + var documentIds = GetAllChangedOrAddedDocumentIds(originalSolution, changedSolution); + var documentIdsAndOptionsToClean = await GetDocumentIdsAndOptionsToCleanAsync().ConfigureAwait(false); + + // Then do a pass where we cleanup semantics. + var cleanedSolution = await RunAllCleanupPassesInOrderAsync( + changedSolution, documentIdsAndOptionsToClean, progress, cancellationToken).ConfigureAwait(false); + + return cleanedSolution; + + async Task> GetDocumentIdsAndOptionsToCleanAsync() + { + using var _ = ArrayBuilder<(DocumentId documentId, CodeCleanupOptions options)>.GetInstance(documentIds.Length, out var documentIdsAndOptions); + foreach (var documentId in documentIds) + { + var document = changedSolution.GetRequiredDocument(documentId); + + // Only care about documents that support syntax. Non-C#/VB files can't be cleaned. + if (document.SupportsSyntaxTree) + { + var codeActionOptions = await document.GetCodeCleanupOptionsAsync(optionsProvider, cancellationToken).ConfigureAwait(false); + documentIdsAndOptions.Add((documentId, codeActionOptions)); + } + } + + return documentIdsAndOptions.ToImmutableAndClear(); + } + } + + internal static async ValueTask CleanupDocumentAsync(Document document, CodeCleanupOptions options, CancellationToken cancellationToken) + { + if (!document.SupportsSyntaxTree) + return document; + + var cleanedSolution = await RunAllCleanupPassesInOrderAsync( + document.Project.Solution, + [(document.Id, options)], + CodeAnalysisProgress.None, + cancellationToken).ConfigureAwait(false); + + return cleanedSolution.GetRequiredDocument(document.Id); + } + + private static async Task RunAllCleanupPassesInOrderAsync( + Solution solution, + ImmutableArray<(DocumentId documentId, CodeCleanupOptions options)> documentIdsAndOptions, + IProgress progress, + CancellationToken cancellationToken) + { + // One item per document per cleanup pass. + progress.AddItems(documentIdsAndOptions.Length * s_cleanupPasses.Length); + + var currentSolution = solution; + foreach (var cleanupPass in s_cleanupPasses) + currentSolution = await RunParallelCleanupPassAsync(currentSolution, cleanupPass).ConfigureAwait(false); + + return currentSolution; + + async Task RunParallelCleanupPassAsync( + Solution solution, Func> cleanupDocumentAsync) + { + // We're about to making a ton of calls to this new solution, including expensive oop calls to get up to + // date compilations, skeletons and SG docs. Create and pin this solution so that all remote calls operate + // on the same fork and do not cause the forked solution to be created and dropped repeatedly. + using var _ = await RemoteKeepAliveSession.CreateAsync(solution, cancellationToken).ConfigureAwait(false); + + var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: documentIdsAndOptions, + produceItems: static async (documentIdAndOptions, callback, args, cancellationToken) => + { + // As we finish each document, update our progress. + using var _ = args.progress.ItemCompletedScope(); + + var (documentId, options) = documentIdAndOptions; + + // Fetch the current state of the document from this fork of the solution. + var document = args.solution.GetRequiredDocument(documentId); + Contract.ThrowIfFalse(document.SupportsSyntaxTree, "GetDocumentIdsAndOptionsAsync should only be returning documents that support syntax"); + + // Now, perform the requested cleanup pass on it. + var cleanedDocument = await args.cleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); + if (cleanedDocument is null || cleanedDocument == document) + return; + + // Now get the cleaned root and pass it back to the consumer. + var newRoot = await cleanedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + callback((documentId, newRoot)); + }, + args: (solution, progress, cleanupDocumentAsync), + cancellationToken).ConfigureAwait(false); + + // Grab all the cleaned roots and produce the new solution snapshot from that. + return solution.WithDocumentSyntaxRoots(changedRoots); + } + } +} diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs index 84bdd989f7768..179de6c20bd69 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeCleanup.Providers; -internal sealed class FormatCodeCleanupProvider(IEnumerable? rules = null) : ICodeCleanupProvider +internal sealed class FormatCodeCleanupProvider(ImmutableArray rules = default) : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Format; diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index a7e5192a9bdf0..c5fccb6afbcfb 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -253,15 +253,10 @@ private static Action> GetRegisterCodeFix private static async Task ApplyChangesAsync( Solution currentSolution, - ImmutableArray<(DocumentId, TextChangeMerger)> docIdsAndMerger, + ImmutableArray<(DocumentId documentId, TextChangeMerger merger)> docIdsAndMerger, CancellationToken cancellationToken) { - foreach (var (documentId, textMerger) in docIdsAndMerger) - { - var newText = await textMerger.GetFinalMergedTextAsync(cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentText(documentId, newText); - } - - return currentSolution; + var docIdsAndTexts = await docIdsAndMerger.SelectAsArrayAsync(async t => (t.documentId, await t.merger.GetFinalMergedTextAsync(cancellationToken).ConfigureAwait(false))).ConfigureAwait(false); + return currentSolution.WithDocumentTexts(docIdsAndTexts); } } diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs index 823220fb6a546..88e8fc3f4d4da 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -74,7 +73,7 @@ public sealed override IEnumerable GetSupportedFixAllScopes() DetermineDiagnosticsAndGetFixedDocumentsAsync); private async Task DetermineDiagnosticsAndGetFixedDocumentsAsync( - FixAllContext fixAllContext, Action<(DocumentId documentId, (SyntaxNode? node, SourceText? text))> callback) + FixAllContext fixAllContext, Func onDocumentFixed) { var cancellationToken = fixAllContext.CancellationToken; @@ -93,15 +92,7 @@ await RoslynParallel.ForEachAsync( return; var newDocument = await this.FixAllAsync(fixAllContext, document, documentDiagnostics).ConfigureAwait(false); - if (newDocument == null || newDocument == document) - return; - - // For documents that support syntax, grab the tree so that we can clean it up later. If it's a - // language that doesn't support that, then just grab the text. - var node = newDocument.SupportsSyntaxTree ? await newDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false) : null; - var text = newDocument.SupportsSyntaxTree ? null : await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - - callback((document.Id, (node, text))); + await onDocumentFixed(document, newDocument).ConfigureAwait(false); }).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs index 1035dcc812517..a58b5618efa87 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -27,29 +26,50 @@ internal static class DocumentBasedFixAllProviderHelpers ImmutableArray fixAllContexts, IProgress progressTracker, string progressTrackerDescription, - Func, Task> getFixedDocumentsAsync) + Func, Task> getFixedDocumentsAsync) where TFixAllContext : IFixAllContext { var cancellationToken = originalFixAllContext.CancellationToken; progressTracker.Report(CodeAnalysisProgress.Description(progressTrackerDescription)); - var solution = originalFixAllContext.Solution; + var originalSolution = originalFixAllContext.Solution; // One work item for each context. progressTracker.AddItems(fixAllContexts.Length); - var (dirtySolution, changedRootDocumentIds) = await GetInitialUncleanedSolutionAsync().ConfigureAwait(false); - return await CleanSolutionAsync(dirtySolution, changedRootDocumentIds).ConfigureAwait(false); + // Do the initial pass to fixup documents. + var dirtySolution = await GetInitialUncleanedSolutionAsync(originalSolution).ConfigureAwait(false); - async Task<(Solution dirtySolution, ImmutableArray changedRootDocumentIds)> GetInitialUncleanedSolutionAsync() + // Now do a pass to clean the fixed documents. + progressTracker.Report(CodeAnalysisProgress.Clear()); + progressTracker.Report(CodeAnalysisProgress.Description(WorkspacesResources.Running_code_cleanup_on_fixed_documents)); + + var cleanedSolution = await CodeAction.CleanSyntaxAndSemanticsAsync( + originalSolution, + dirtySolution, + originalFixAllContext.State.CodeActionOptionsProvider, + progressTracker, + cancellationToken).ConfigureAwait(false); + + // Once we clean the document, we get the text of it and insert that back into the final solution. This way we + // can release both the original fixed tree, and the cleaned tree (both of which can be much more expensive than + // just text). + var cleanedTexts = await CodeAction.GetAllChangedOrAddedDocumentIds(originalSolution, cleanedSolution) + .SelectAsArrayAsync(async documentId => (documentId, await cleanedSolution.GetRequiredDocument(documentId).GetTextAsync(cancellationToken).ConfigureAwait(false))) + .ConfigureAwait(false); + + var finalSolution = cleanedSolution.WithDocumentTexts(cleanedTexts); + return finalSolution; + + async Task GetInitialUncleanedSolutionAsync(Solution originalSolution) { // First, iterate over all contexts, and collect all the changes for each of them. We'll be making a lot of // calls to the remote server to compute diagnostics and changes. So keep a single connection alive to it // so we never resync or recompute anything. - using var _ = await RemoteKeepAliveSession.CreateAsync(solution, cancellationToken).ConfigureAwait(false); + using var _ = await RemoteKeepAliveSession.CreateAsync(originalSolution, cancellationToken).ConfigureAwait(false); - return await ProducerConsumer<(DocumentId documentId, (SyntaxNode? node, SourceText? text))>.RunParallelAsync( + var changedRootsAndTexts = await ProducerConsumer<(DocumentId documentId, (SyntaxNode? node, SourceText? text))>.RunParallelAsync( source: fixAllContexts, produceItems: static async (fixAllContext, callback, args, cancellationToken) => { @@ -59,92 +79,35 @@ internal static class DocumentBasedFixAllProviderHelpers Contract.ThrowIfFalse( fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or FixAllScope.ContainingMember or FixAllScope.ContainingType); - await args.getFixedDocumentsAsync(fixAllContext, callback).ConfigureAwait(false); - }, - consumeItems: static async (stream, args, cancellationToken) => - { - var currentSolution = args.solution; - using var _ = ArrayBuilder.GetInstance(out var changedRootDocumentIds); - - // Next, go and insert those all into the solution so all the docs in this particular project - // point at the new trees (or text). At this point though, the trees have not been cleaned up. - // We don't cleanup the documents as they are created, or one at a time as we add them, as that - // would cause us to run cleanup on N different solution forks (which would be very expensive). - // Instead, by adding all the changed documents to one solution, and then cleaning *those* we - // only perform cleanup semantics on one forked solution. - await foreach (var (docId, (newRoot, newText)) in stream) - { - // If we produced a new root (as opposed to new text), keep track of that doc-id so that we - // can clean this doc later. - if (newRoot != null) - changedRootDocumentIds.Add(docId); - - currentSolution = newRoot != null - ? currentSolution.WithDocumentSyntaxRoot(docId, newRoot) - : currentSolution.WithDocumentText(docId, newText!); - } - - return (currentSolution, changedRootDocumentIds.ToImmutableAndClear()); + // Defer to the FixAllProvider to actually compute each fixed document. + await args.getFixedDocumentsAsync( + fixAllContext, + async (originalDocument, newDocument) => + { + if (newDocument == null || newDocument == originalDocument) + return; + + var newRoot = newDocument.SupportsSyntaxTree ? await newDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false) : null; + var newText = newDocument.SupportsSyntaxTree ? null : await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + callback((newDocument.Id, (newRoot, newText))); + }).ConfigureAwait(false); }, - args: (getFixedDocumentsAsync, progressTracker, solution), + args: (getFixedDocumentsAsync, progressTracker, originalSolution), cancellationToken).ConfigureAwait(false); - } - - async Task CleanSolutionAsync(Solution dirtySolution, ImmutableArray changedRootDocumentIds) - { - if (changedRootDocumentIds.IsEmpty) - return dirtySolution; - - // Clear out the progress so far. We're starting a new progress pass for the final cleanup. - progressTracker.Report(CodeAnalysisProgress.Clear()); - progressTracker.Report(CodeAnalysisProgress.AddIncompleteItems(changedRootDocumentIds.Length, WorkspacesResources.Running_code_cleanup_on_fixed_documents)); - - // We're about to making a ton of calls to this new solution, including expensive oop calls to get up to - // date compilations, skeletons and SG docs. Create and pin this solution so that all remote calls operate - // on the same fork and do not cause the forked solution to be created and dropped repeatedly. - using var _ = await RemoteKeepAliveSession.CreateAsync(dirtySolution, cancellationToken).ConfigureAwait(false); - - // Next, go and cleanup any trees we inserted. Once we clean the document, we get the text of it and insert that - // back into the final solution. This way we can release both the original fixed tree, and the cleaned tree - // (both of which can be much more expensive than just text). - // - // Do this in parallel across all the documents that were fixed and resulted in a new tree (as opposed to new - // text). - return await ProducerConsumer<(DocumentId docId, SourceText sourceText)>.RunParallelAsync( - source: changedRootDocumentIds, - produceItems: static async (documentId, callback, args, cancellationToken) => - { - using var _ = args.progressTracker.ItemCompletedScope(); - var dirtyDocument = args.dirtySolution.GetRequiredDocument(documentId); - var cleanedDocument = await PostProcessCodeAction.Instance.PostProcessChangesAsync(dirtyDocument, cancellationToken).ConfigureAwait(false); - var cleanedText = await cleanedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - callback((dirtyDocument.Id, cleanedText)); - }, - consumeItems: static async (results, args, cancellationToken) => - { - // Finally, apply the cleaned documents to the solution. - var finalSolution = args.dirtySolution; - await foreach (var (docId, cleanedText) in results) - finalSolution = finalSolution.WithDocumentText(docId, cleanedText); - - return finalSolution; - }, - args: (dirtySolution, progressTracker), - cancellationToken).ConfigureAwait(false); + // Next, go and insert those all into the solution so all the docs in this particular project point + // at the new trees (or text). At this point though, the trees have not been semantically cleaned + // up. We don't cleanup the documents as they are created, or one at a time as we add them, as that + // would cause us to run semantic cleanup on N different solution forks (which would be very + // expensive as we'd fork, produce semantics, fork, produce semantics, etc. etc.). Instead, by + // adding all the changed documents to one solution, and then cleaning *those* we only perform + // cleanup semantics on one forked solution. + var changedRoots = changedRootsAndTexts.SelectAsArray(t => t.Item2.node != null, t => (t.documentId, t.Item2.node!, PreservationMode.PreserveValue)); + var changedTexts = changedRootsAndTexts.SelectAsArray(t => t.Item2.text != null, t => (t.documentId, t.Item2.text!, PreservationMode.PreserveValue)); + + return originalSolution + .WithDocumentSyntaxRoots(changedRoots) + .WithDocumentTexts(changedTexts); } } - - /// - /// Dummy class just to get access to - /// - private class PostProcessCodeAction : CodeAction - { - public static readonly PostProcessCodeAction Instance = new(); - - public override string Title => ""; - - public new Task PostProcessChangesAsync(Document document, CancellationToken cancellationToken) - => base.PostProcessChangesAsync(document, cancellationToken); - } } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs index 7bb3a4c6c2526..6dbcf20bc1002 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs @@ -8,8 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -28,20 +26,15 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings; /// /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 /// -internal abstract class DocumentBasedFixAllProvider : FixAllProvider +internal abstract class DocumentBasedFixAllProvider(ImmutableArray supportedFixAllScopes) : FixAllProvider { - private readonly ImmutableArray _supportedFixAllScopes; + private readonly ImmutableArray _supportedFixAllScopes = supportedFixAllScopes; protected DocumentBasedFixAllProvider() : this(DefaultSupportedFixAllScopes) { } - protected DocumentBasedFixAllProvider(ImmutableArray supportedFixAllScopes) - { - _supportedFixAllScopes = supportedFixAllScopes; - } - /// /// Produce a suitable title for the fix-all this type creates in . Override this if customizing that title is desired. @@ -81,13 +74,12 @@ public sealed override IEnumerable GetSupportedFixAllScopes() GetFixedDocumentsAsync); /// - /// Attempts to apply fix all operations returning, for each updated document, either - /// the new syntax root for that document or its new text. Syntax roots are returned for documents that support - /// them, and are used to perform a final cleanup pass for formatting/simplication/etc. Text is returned for - /// documents that don't support syntax. + /// Attempts to apply fix all operations returning, for each updated document, either the new syntax root for that + /// document or its new text. Syntax roots are returned for documents that support them, and are used to perform a + /// final cleanup pass for formatting/simplification/etc. Text is returned for documents that don't support syntax. /// private async Task GetFixedDocumentsAsync( - FixAllContext fixAllContext, Action<(DocumentId documentId, (SyntaxNode? node, SourceText? text))> callback) + FixAllContext fixAllContext, Func onDocumentFixed) { Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project or FixAllScope.ContainingMember or FixAllScope.ContainingType); @@ -104,15 +96,7 @@ await RoslynParallel.ForEachAsync( { var (document, spans) = tuple; var newDocument = await this.FixAllAsync(fixAllContext, document, spans).ConfigureAwait(false); - if (newDocument == null || newDocument == document) - return; - - // For documents that support syntax, grab the tree so that we can clean it up later. If it's a - // language that doesn't support that, then just grab the text. - var node = newDocument.SupportsSyntaxTree ? await newDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false) : null; - var text = newDocument.SupportsSyntaxTree ? null : await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - - callback((document.Id, (node, text))); + await onDocumentFixed(document, newDocument).ConfigureAwait(false); }).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs b/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs index db82f43f5df66..9443bf285ee4d 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs @@ -5,31 +5,35 @@ using System; using System.Composition; using System.IO; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; +using System.Collections.Immutable; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Host; [ExportWorkspaceServiceFactory(typeof(IAnalyzerAssemblyLoaderProvider)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class DefaultAnalyzerAssemblyLoaderServiceFactory() : IWorkspaceServiceFactory +internal sealed class DefaultAnalyzerAssemblyLoaderServiceFactory([ImportMany] IEnumerable externalResolvers) : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new DefaultAnalyzerAssemblyLoaderProvider(workspaceServices.Workspace.Kind ?? "default"); + => new DefaultAnalyzerAssemblyLoaderProvider(workspaceServices.Workspace.Kind ?? "default", [.. externalResolvers]); - private sealed class DefaultAnalyzerAssemblyLoaderProvider(string workspaceKind) : IAnalyzerAssemblyLoaderProvider + private sealed class DefaultAnalyzerAssemblyLoaderProvider(string workspaceKind, ImmutableArray externalResolvers) : IAnalyzerAssemblyLoaderProvider { - private readonly DefaultAnalyzerAssemblyLoader _loader = new(); + private readonly DefaultAnalyzerAssemblyLoader _loader = new(externalResolvers); /// /// We include the of the workspace in the path we produce. That way we don't /// collide in the common case of a normal host workspace and OOP workspace running together. This avoids an /// annoying exception as each will try to clean up this directory, throwing exceptions because the other is - /// locking it. The exception is fine, since the cleanup is just hygenic and isn't intended to be needed for + /// locking it. The exception is fine, since the cleanup is just hygienic and isn't intended to be needed for /// correctness. But it is annoying and does cause noise in our perf test harness. /// private readonly IAnalyzerAssemblyLoader _shadowCopyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader( - Path.Combine(Path.GetTempPath(), "CodeAnalysis", "WorkspacesAnalyzerShadowCopies", workspaceKind)); + Path.Combine(Path.GetTempPath(), "CodeAnalysis", "WorkspacesAnalyzerShadowCopies", workspaceKind), + externalResolvers: externalResolvers); public IAnalyzerAssemblyLoader GetLoader(in AnalyzerAssemblyLoaderOptions options) => options.ShadowCopy ? _shadowCopyLoader : _loader; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 380a3c3bf55f0..dc87229e40fca 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; @@ -28,7 +29,15 @@ internal static partial class DependentProjectsFinder /// Cache from the for a particular to the /// name of the defined by it. /// - private static ImmutableDictionary s_metadataIdToAssemblyName = ImmutableDictionary.Empty; + private static readonly Dictionary s_metadataIdToAssemblyName = new(); + private static readonly SemaphoreSlim s_metadataIdToAssemblyNameGate = new(initialCount: 1); + + private static readonly ConditionalWeakTable< + Solution, + Dictionary< + (IAssemblySymbol assembly, Project? sourceProject, SymbolVisibility visibility), + ImmutableArray<(Project project, bool hasInternalsAccess)>>> s_solutionToDependentProjectMap = new(); + private static readonly SemaphoreSlim s_solutionToDependentProjectMapGate = new(initialCount: 1); public static async Task> GetDependentProjectsAsync( Solution solution, ImmutableArray symbols, IImmutableSet projects, CancellationToken cancellationToken) @@ -128,24 +137,56 @@ private static async Task> GetDependentProjectsWorkerAsy SymbolVisibility visibility, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); + var dictionary = s_solutionToDependentProjectMap.GetValue(solution, static _ => new()); - var dependentProjects = new HashSet<(Project, bool hasInternalsAccess)>(); + var key = (symbolOrigination.assembly, symbolOrigination.sourceProject, visibility); + ImmutableArray<(Project project, bool hasInternalsAccess)> dependentProjects; - // If a symbol was defined in source, then it is always visible to the project it - // was defined in. - if (symbolOrigination.sourceProject != null) - dependentProjects.Add((symbolOrigination.sourceProject, hasInternalsAccess: true)); + // Check cache first. + using (await s_solutionToDependentProjectMapGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + if (dictionary.TryGetValue(key, out dependentProjects)) + return dependentProjects; + } + + // Compute if not in cache. + dependentProjects = await ComputeDependentProjectsWorkerAsync( + solution, symbolOrigination, visibility, cancellationToken).ConfigureAwait(false); - // If it's not private, then we need to find possible references. - if (visibility != SymbolVisibility.Private) - AddNonSubmissionDependentProjects(solution, symbolOrigination, dependentProjects, cancellationToken); + // Try to add to cache, returning existing value if another thread already added it. + using (await s_solutionToDependentProjectMapGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + return dictionary.GetOrAdd(key, dependentProjects); + } + + static async Task> ComputeDependentProjectsWorkerAsync( + Solution solution, + (IAssemblySymbol assembly, Project? sourceProject) symbolOrigination, + SymbolVisibility visibility, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using var _ = PooledHashSet<(Project, bool hasInternalsAccess)>.GetInstance(out var dependentProjects); + + // If a symbol was defined in source, then it is always visible to the project it + // was defined in. + if (symbolOrigination.sourceProject != null) + dependentProjects.Add((symbolOrigination.sourceProject, hasInternalsAccess: true)); + + // If it's not private, then we need to find possible references. + if (visibility != SymbolVisibility.Private) + { + await AddNonSubmissionDependentProjectsAsync( + solution, symbolOrigination, dependentProjects, cancellationToken).ConfigureAwait(false); + } - // submission projects are special here. The fields generated inside the Script object is private, but - // further submissions can bind to them. - await AddSubmissionDependentProjectsAsync(solution, symbolOrigination.sourceProject, dependentProjects, cancellationToken).ConfigureAwait(false); + // submission projects are special here. The fields generated inside the Script object is private, but + // further submissions can bind to them. + await AddSubmissionDependentProjectsAsync(solution, symbolOrigination.sourceProject, dependentProjects, cancellationToken).ConfigureAwait(false); - return [.. dependentProjects]; + return [.. dependentProjects]; + } } private static async Task AddSubmissionDependentProjectsAsync( @@ -154,7 +195,7 @@ private static async Task AddSubmissionDependentProjectsAsync( if (sourceProject?.IsSubmission != true) return; - var projectIdsToReferencingSubmissionIds = new Dictionary>(); + using var _1 = PooledDictionary>.GetInstance(out var projectIdsToReferencingSubmissionIds); // search only submission project foreach (var projectId in solution.ProjectIds) @@ -173,15 +214,7 @@ private static async Task AddSubmissionDependentProjectsAsync( { var referencedProject = solution.GetProject(previous.Assembly, cancellationToken); if (referencedProject != null) - { - if (!projectIdsToReferencingSubmissionIds.TryGetValue(referencedProject.Id, out var referencingSubmissions)) - { - referencingSubmissions = []; - projectIdsToReferencingSubmissionIds.Add(referencedProject.Id, referencingSubmissions); - } - - referencingSubmissions.Add(project.Id); - } + projectIdsToReferencingSubmissionIds.MultiAdd(referencedProject.Id, project.Id); } } } @@ -191,7 +224,7 @@ private static async Task AddSubmissionDependentProjectsAsync( // and 2, even though 2 doesn't have a direct reference to 1. Hence we need to take // our current set of projects and find the transitive closure over backwards // submission previous references. - using var _ = ArrayBuilder.GetInstance(out var projectIdsToProcess); + using var _2 = ArrayBuilder.GetInstance(out var projectIdsToProcess); foreach (var dependentProject in dependentProjects.Select(dp => dp.project.Id)) projectIdsToProcess.Push(dependentProject); @@ -221,7 +254,7 @@ private static bool IsInternalsVisibleToAttribute(AttributeData attr) attrType.ContainingNamespace.ContainingNamespace.ContainingNamespace.ContainingNamespace?.IsGlobalNamespace == true; } - private static void AddNonSubmissionDependentProjects( + private static async Task AddNonSubmissionDependentProjectsAsync( Solution solution, (IAssemblySymbol assembly, Project? sourceProject) symbolOrigination, HashSet<(Project project, bool hasInternalsAccess)> dependentProjects, @@ -235,7 +268,7 @@ private static void AddNonSubmissionDependentProjects( foreach (var project in solution.Projects) { if (!project.SupportsCompilation || - !HasReferenceTo(symbolOrigination, project, cancellationToken)) + !await HasReferenceToAsync(symbolOrigination, project, cancellationToken).ConfigureAwait(false)) { continue; } @@ -270,7 +303,7 @@ private static HashSet GetInternalsVisibleToSet(IAssemblySymbol assembly return set; } - private static bool HasReferenceTo( + private static async Task HasReferenceToAsync( (IAssemblySymbol assembly, Project? sourceProject) symbolOrigination, Project project, CancellationToken cancellationToken) @@ -284,10 +317,11 @@ private static bool HasReferenceTo( return project.ProjectReferences.Any(p => p.ProjectId == symbolOrigination.sourceProject.Id); // Otherwise, if the symbol is from metadata, see if the project's compilation references that metadata assembly. - return HasReferenceToAssembly(project, symbolOrigination.assembly.Name, cancellationToken); + return await HasReferenceToAssemblyAsync( + project, symbolOrigination.assembly.Name, cancellationToken).ConfigureAwait(false); } - private static bool HasReferenceToAssembly(Project project, string assemblyName, CancellationToken cancellationToken) + private static async Task HasReferenceToAssemblyAsync(Project project, string assemblyName, CancellationToken cancellationToken) { Contract.ThrowIfFalse(project.SupportsCompilation); @@ -307,31 +341,50 @@ private static bool HasReferenceToAssembly(Project project, string assemblyName, if (metadataId is null) continue; - if (!s_metadataIdToAssemblyName.TryGetValue(metadataId, out var name)) + using (await s_metadataIdToAssemblyNameGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - uncomputedReferences.Add((peReference, metadataId)); - continue; + if (s_metadataIdToAssemblyName.TryGetValue(metadataId, out var name)) + { + // We already know the assembly name for this metadata id. If it matches the one we're looking for, + // we're done. Otherwise, keep looking. + if (name == assemblyName) + return true; + else + continue; + } } - if (name == assemblyName) - return true; + // We didn't know the name for the metadata id. Add it to the list of things we need to compute below. + uncomputedReferences.Add((peReference, metadataId)); } if (uncomputedReferences.Count == 0) return false; - Compilation? compilation = null; + var compilation = CreateCompilation(project); foreach (var (peReference, metadataId) in uncomputedReferences) { cancellationToken.ThrowIfCancellationRequested(); - if (!s_metadataIdToAssemblyName.TryGetValue(metadataId, out var name)) + // Attempt to get the assembly name for this pe-reference. If we fail, we still want to add that info into + // the dictionary (by mapping us to 'null'). That way we don't keep trying to compute it over and over. + var name = compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol { Name: string metadataAssemblyName } + ? metadataAssemblyName + : null; + + using (await s_metadataIdToAssemblyNameGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - // Defer creating the compilation till needed. - CreateCompilation(project, ref compilation); - if (compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol { Name: string metadataAssemblyName }) - name = ImmutableInterlocked.GetOrAdd(ref s_metadataIdToAssemblyName, metadataId, metadataAssemblyName); + // Overwrite an existing null name with a non-null one. + if (s_metadataIdToAssemblyName.TryGetValue(metadataId, out var existingName) && + existingName == null && + name != null) + { + s_metadataIdToAssemblyName[metadataId] = name; + } + + // Return whatever is in the map, adding ourselves if something is not already there. + name = s_metadataIdToAssemblyName.GetOrAdd(metadataId, name); } if (name == assemblyName) @@ -340,18 +393,15 @@ private static bool HasReferenceToAssembly(Project project, string assemblyName, return false; - static void CreateCompilation(Project project, [NotNull] ref Compilation? compilation) + static Compilation CreateCompilation(Project project) { - if (compilation != null) - return; - // Use the project's compilation if it has one. - if (project.TryGetCompilation(out compilation)) - return; + if (project.TryGetCompilation(out var compilation)) + return compilation; // Perf: check metadata reference using newly created empty compilation with only metadata references. var factory = project.Services.GetRequiredService(); - compilation = factory + return factory .CreateCompilation(project.AssemblyName, project.CompilationOptions!) .AddReferences(project.MetadataReferences); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesDocumentState.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesDocumentState.cs index 7a2fb1f8c7d6a..5528b7b26bbd2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesDocumentState.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesDocumentState.cs @@ -27,6 +27,10 @@ internal sealed class FindReferencesDocumentState( public SyntaxTree SyntaxTree => this.SemanticModel.SyntaxTree; public Solution Solution => this.Document.Project.Solution; - public ISyntaxFactsService SyntaxFacts => this.Document.GetRequiredLanguageService(); - public ISemanticFactsService SemanticFacts => this.Document.GetRequiredLanguageService(); + + // These are expensive enough (in the GetRequiredLanguageService call) that we cache this up front in stead of + // computing on demand. + + public ISyntaxFactsService SyntaxFacts { get; } = cache.Document.GetRequiredLanguageService(); + public ISemanticFactsService SemanticFacts { get; } = cache.Document.GetRequiredLanguageService(); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 99787ba6b637f..cd7135df2fa61 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -603,18 +603,15 @@ protected static SymbolUsageInfo GetSymbolUsageInfo( var semanticFacts = state.SemanticFacts; var semanticModel = state.SemanticModel; - return IsInNamespaceOrTypeContext() - ? SymbolUsageInfo.Create(GetTypeOrNamespaceUsageInfo()) - : GetSymbolUsageInfoCommon(); + var topNameNode = node; + while (syntaxFacts.IsQualifiedName(topNameNode.Parent)) + topNameNode = topNameNode.Parent; - bool IsInNamespaceOrTypeContext() - { - var current = node; - while (syntaxFacts.IsQualifiedName(current.Parent)) - current = current.Parent; + var isInNamespaceNameContext = syntaxFacts.IsBaseNamespaceDeclaration(topNameNode.Parent); - return syntaxFacts.IsInNamespaceOrTypeContext(current); - } + return syntaxFacts.IsInNamespaceOrTypeContext(topNameNode) + ? SymbolUsageInfo.Create(GetTypeOrNamespaceUsageInfo()) + : GetSymbolUsageInfoCommon(); // Local functions. TypeOrNamespaceUsageInfo GetTypeOrNamespaceUsageInfo() @@ -623,7 +620,7 @@ TypeOrNamespaceUsageInfo GetTypeOrNamespaceUsageInfo() ? TypeOrNamespaceUsageInfo.Qualified : TypeOrNamespaceUsageInfo.None; - if (semanticFacts.IsNamespaceDeclarationNameContext(semanticModel, node.SpanStart, cancellationToken)) + if (isInNamespaceNameContext) { usageInfo |= TypeOrNamespaceUsageInfo.NamespaceDeclaration; } @@ -697,24 +694,18 @@ SymbolUsageInfo GetSymbolUsageInfoCommon() { case SymbolKind.Namespace: var namespaceUsageInfo = TypeOrNamespaceUsageInfo.None; - if (semanticFacts.IsNamespaceDeclarationNameContext(semanticModel, node.SpanStart, cancellationToken)) - { + if (isInNamespaceNameContext) namespaceUsageInfo |= TypeOrNamespaceUsageInfo.NamespaceDeclaration; - } if (IsNodeOrAnyAncestorLeftSideOfDot(node, syntaxFacts)) - { namespaceUsageInfo |= TypeOrNamespaceUsageInfo.Qualified; - } return SymbolUsageInfo.Create(namespaceUsageInfo); case SymbolKind.NamedType: var typeUsageInfo = TypeOrNamespaceUsageInfo.None; if (IsNodeOrAnyAncestorLeftSideOfDot(node, syntaxFacts)) - { typeUsageInfo |= TypeOrNamespaceUsageInfo.Qualified; - } return SymbolUsageInfo.Create(typeUsageInfo); @@ -726,9 +717,7 @@ SymbolUsageInfo GetSymbolUsageInfoCommon() case SymbolKind.Local: var valueUsageInfo = ValueUsageInfo.Read; if (semanticFacts.IsWrittenTo(semanticModel, node, cancellationToken)) - { valueUsageInfo |= ValueUsageInfo.Write; - } return SymbolUsageInfo.Create(valueUsageInfo); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs index 9e0d20f947dfd..4d25e28b3a401 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs @@ -209,6 +209,7 @@ private void FindReferencesInImplicitObjectCreationExpression( ? -1 : symbol.Parameters.Length; + var implicitObjectKind = state.SyntaxFacts.SyntaxKinds.ImplicitObjectCreationExpression; FindReferencesInDocument(state, IsRelevantDocument, CollectMatchingReferences, processResult, processResultData, cancellationToken); return; @@ -218,12 +219,11 @@ static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) void CollectMatchingReferences( SyntaxNode node, FindReferencesDocumentState state, Action processResult, TData processResultData) { - var syntaxFacts = state.SyntaxFacts; - if (!syntaxFacts.IsImplicitObjectCreationExpression(node)) + if (node.RawKind != implicitObjectKind) return; // if there are too few or too many arguments, then don't bother checking. - var actualArgumentCount = syntaxFacts.GetArgumentsOfObjectCreationExpression(node).Count; + var actualArgumentCount = state.SyntaxFacts.GetArgumentsOfObjectCreationExpression(node).Count; if (actualArgumentCount < minimumArgumentCount || actualArgumentCount > maximumArgumentCount) return; diff --git a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs index 00ea1fc9737f8..e56e065978dd7 100644 --- a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs +++ b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs @@ -18,6 +18,6 @@ internal abstract class AbstractFormattingService : IFormattingService public Task FormatAsync(Document document, IEnumerable? spans, LineFormattingOptions lineFormattingOptions, SyntaxFormattingOptions? syntaxFormattingOptions, CancellationToken cancellationToken) { Contract.ThrowIfNull(syntaxFormattingOptions); - return Formatter.FormatAsync(document, spans, syntaxFormattingOptions, rules: null, cancellationToken); + return Formatter.FormatAsync(document, spans, syntaxFormattingOptions, rules: default, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 6803e4697a6df..b68782503aa8a 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -52,7 +52,7 @@ public static Task FormatAsync(Document document, OptionSet? options = #pragma warning restore internal static Task FormatAsync(Document document, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, spans: null, options, rules: null, cancellationToken); + => FormatAsync(document, spans: null, options, rules: default, cancellationToken); /// /// Formats the whitespace in an area of a document corresponding to a text span. @@ -68,7 +68,7 @@ public static Task FormatAsync(Document document, TextSpan span, Optio #pragma warning restore internal static Task FormatAsync(Document document, TextSpan span, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, [span], options, rules: null, cancellationToken); + => FormatAsync(document, [span], options, rules: default, cancellationToken); /// /// Formats the whitespace in areas of a document corresponding to multiple non-overlapping spans. @@ -90,7 +90,7 @@ public static async Task FormatAsync(Document document, IEnumerable FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; @@ -106,19 +106,19 @@ internal static async Task FormatAsync(Document document, IEnumerable< /// An optional cancellation token. /// The formatted document. public static Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? options = null, CancellationToken cancellationToken = default) - => FormatAsync(document, annotation, options, rules: null, cancellationToken: cancellationToken); + => FormatAsync(document, annotation, options, rules: default, cancellationToken: cancellationToken); internal static Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, annotation, options, rules: null, cancellationToken); + => FormatAsync(document, annotation, options, rules: default, cancellationToken); - internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; return document.WithSyntaxRoot(Format(root, annotation, services, options, rules, cancellationToken)); } - internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? optionSet, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? optionSet, ImmutableArray rules, CancellationToken cancellationToken) { if (document == null) { @@ -150,12 +150,12 @@ internal static async Task FormatAsync(Document document, SyntaxAnnota /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, annotation, workspace, options, rules: null, cancellationToken); + => Format(node, annotation, workspace, options, rules: default, cancellationToken); internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, annotation, services, options, rules: null, cancellationToken); + => Format(node, annotation, services, options, rules: default, cancellationToken); - private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -175,7 +175,7 @@ private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, W return Format(node, GetAnnotatedSpans(node, annotation), workspace, options, rules, cancellationToken); } - internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => Format(node, GetAnnotatedSpans(node, annotation), services, options, rules, cancellationToken); /// @@ -187,10 +187,10 @@ internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, [node.FullSpan], workspace, options, rules: null, cancellationToken); + => Format(node, [node.FullSpan], workspace, options, rules: default, cancellationToken); internal static SyntaxNode Format(SyntaxNode node, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [node.FullSpan], services, options, rules: null, cancellationToken); + => Format(node, [node.FullSpan], services, options, rules: default, cancellationToken); /// /// Formats the whitespace in areas of a syntax tree identified by a span. @@ -202,10 +202,10 @@ internal static SyntaxNode Format(SyntaxNode node, SolutionServices services, Sy /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, [span], workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [span], workspace, options, rules: default, cancellationToken: cancellationToken); internal static SyntaxNode Format(SyntaxNode node, TextSpan span, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [span], services, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [span], services, options, rules: default, cancellationToken: cancellationToken); /// /// Formats the whitespace in areas of a syntax tree identified by multiple non-overlapping spans. @@ -217,18 +217,18 @@ internal static SyntaxNode Format(SyntaxNode node, TextSpan span, SolutionServic /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, spans, workspace, options, rules: default, cancellationToken: cancellationToken); - private static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null ? node : formattingResult.GetFormattedRoot(cancellationToken); } - internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, services, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); - private static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -252,7 +252,7 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, return languageFormatter.GetFormattingResult(node, spans, formattingOptions, rules, cancellationToken); } - internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var formatter = services.GetRequiredLanguageService(node.Language); return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken); @@ -267,10 +267,10 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerab /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [node.FullSpan], workspace, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], workspace, options, rules: default, cancellationToken: cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => GetFormattedTextChanges(node, [node.FullSpan], services, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], services, options, rules: default, cancellationToken: cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -282,10 +282,10 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, Solut /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [span], workspace, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, [span], workspace, options, rules: default, cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [span], services, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, [span], services, options, rules: default, cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -297,12 +297,12 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, TextS /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, spans, workspace, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, spans, workspace, options, rules: default, cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, spans, services, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, spans, services, options, rules: default, cancellationToken); - private static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null @@ -310,7 +310,7 @@ private static IList GetFormattedTextChanges(SyntaxNode node, IEnume : formattingResult.GetTextChanges(cancellationToken); } - internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken = default) + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken = default) { var formatter = services.GetRequiredLanguageService(node.Language); return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken).GetTextChanges(cancellationToken); diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs index 2d7adffa24ae2..3f76b79b27406 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs @@ -5,8 +5,10 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -16,9 +18,9 @@ internal abstract class AbstractLinkedFileMergeConflictCommentAdditionService : { internal abstract string GetConflictCommentText(string header, string beforeString, string afterString); - public IEnumerable CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges) + public ImmutableArray CreateEdits(SourceText originalSourceText, ArrayBuilder unmergedChanges) { - var commentChanges = new List(); + using var _ = ArrayBuilder.GetInstance(out var commentChanges); foreach (var documentWithChanges in unmergedChanges) { @@ -28,12 +30,12 @@ public IEnumerable CreateEdits(SourceText originalSourceText, IEnume commentChanges.AddRange(comments); } - return commentChanges; + return commentChanges.ToImmutableAndClear(); } - private static IEnumerable> PartitionChangesForDocument(IEnumerable changes, SourceText originalSourceText) + private static List> PartitionChangesForDocument(IEnumerable changes, SourceText originalSourceText) { - var partitionedChanges = new List>(); + var partitionedChanges = new List>(); var currentPartition = new List { changes.First() diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs index e5c4f81e1d986..2e84348dbda34 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs @@ -2,14 +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.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; internal interface IMergeConflictHandler { - IEnumerable CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges); + ImmutableArray CreateEdits(SourceText originalSourceText, ArrayBuilder unmergedChanges); } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 1f7fb23a853f3..f55837400141f 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -2,10 +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; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -17,141 +14,156 @@ namespace Microsoft.CodeAnalysis; +using DocumentAndHashBuilder = ArrayBuilder<(Document newDocument, ImmutableArray newContentHash)>; + internal sealed class LinkedFileDiffMergingSession(Solution oldSolution, Solution newSolution, SolutionChanges solutionChanges) { - internal async Task MergeDiffsAsync(IMergeConflictHandler mergeConflictHandler, CancellationToken cancellationToken) + internal async Task MergeDiffsAsync(IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { - var sessionInfo = new LinkedFileDiffMergingSessionInfo(); + using var _1 = PooledDictionary.GetInstance(out var filePathToNewDocumentsAndHashes); + try + { + foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) + { + // Don't need to do any merging whatsoever for documents that are not linked files. + var newDocument = newSolution.GetRequiredDocument(documentId); + var relatedDocumentIds = newSolution.GetRelatedDocumentIds(newDocument.Id); + if (relatedDocumentIds.Length == 1) + continue; - var linkedDocumentGroupsWithChanges = solutionChanges - .GetProjectChanges() - .SelectMany(p => p.GetChangedDocuments()) - .GroupBy(d => oldSolution.GetDocument(d).FilePath, StringComparer.OrdinalIgnoreCase); + var filePath = newDocument.FilePath; + Contract.ThrowIfNull(filePath); - var linkedFileMergeResults = new List(); + var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static (_, capacity) => DocumentAndHashBuilder.GetInstance(capacity), relatedDocumentIds.Length); - var updatedSolution = newSolution; - foreach (var linkedDocumentsWithChanges in linkedDocumentGroupsWithChanges) - { - var documentInNewSolution = newSolution.GetDocument(linkedDocumentsWithChanges.First()); + var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var newContentHash = newText.GetContentHash(); + // Ignore any linked documents that we have the same contents as. + if (newDocumentsAndHashes.Any(t => t.newContentHash.SequenceEqual(newContentHash))) + continue; - // Ensure the first document in the group is the first in the list of - var allLinkedDocuments = documentInNewSolution.GetLinkedDocumentIds().Add(documentInNewSolution.Id); - if (allLinkedDocuments.Length == 1) - { - continue; + newDocumentsAndHashes.Add((newDocument, newContentHash)); } - SourceText mergedText; - if (linkedDocumentsWithChanges.Count() > 1) - { - var mergeGroupResult = await MergeLinkedDocumentGroupAsync(allLinkedDocuments, linkedDocumentsWithChanges, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); - linkedFileMergeResults.Add(mergeGroupResult); - mergedText = mergeGroupResult.MergedSourceText; - } - else - { - mergedText = await newSolution.GetDocument(linkedDocumentsWithChanges.Single()).GetValueTextAsync(cancellationToken).ConfigureAwait(false); - } + var updatedSolution = newSolution; + using var _ = ArrayBuilder.GetInstance( + filePathToNewDocumentsAndHashes.Count(static kvp => kvp.Value.Count > 1), + out var linkedFileMergeResults); - foreach (var documentId in allLinkedDocuments) + foreach (var (filePath, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) { - updatedSolution = updatedSolution.WithDocumentText(documentId, mergedText); + Contract.ThrowIfTrue(newDocumentsAndHashes.Count == 0); + + // Don't need to do anything if this document has no linked siblings. + var firstNewDocument = newDocumentsAndHashes[0].newDocument; + + var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); + Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop."); + + if (newDocumentsAndHashes.Count == 1) + { + // The file has linked siblings, but we collapsed down to only one actual document change. Ensure that + // any linked files have that same content as well. + var firstSourceText = await firstNewDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, firstSourceText))); + } + else + { + // Otherwise, merge the changes and set all the linked files to that merged content. + var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, mergeConflictHandler, cancellationToken).ConfigureAwait(false); + linkedFileMergeResults.Add(mergeGroupResult); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, mergeGroupResult.MergedSourceText))); + } } - } - return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); + return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); + } + finally + { + foreach (var (_, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) + newDocumentsAndHashes.Free(); + } } private async Task MergeLinkedDocumentGroupAsync( - IEnumerable allLinkedDocuments, - IEnumerable linkedDocumentGroup, - LinkedFileDiffMergingSessionInfo sessionInfo, - IMergeConflictHandler mergeConflictHandler, + DocumentAndHashBuilder newDocumentsAndHashes, + IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { - var groupSessionInfo = new LinkedFileGroupSessionInfo(); + Contract.ThrowIfTrue(newDocumentsAndHashes.Count < 2); // Automatically merge non-conflicting diffs while collecting the conflicting diffs var textDifferencingService = oldSolution.Services.GetRequiredService(); - var appliedChanges = await textDifferencingService.GetTextChangesAsync(oldSolution.GetDocument(linkedDocumentGroup.First()), newSolution.GetDocument(linkedDocumentGroup.First()), cancellationToken).ConfigureAwait(false); - var unmergedChanges = new List(); - foreach (var documentId in linkedDocumentGroup.Skip(1)) + var firstNewDocument = newDocumentsAndHashes[0].newDocument; + var firstOldDocument = oldSolution.GetRequiredDocument(firstNewDocument.Id); + var firstOldSourceText = await firstOldDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + + var allTextChangesAcrossLinkedFiles = await textDifferencingService.GetTextChangesAsync( + firstOldDocument, firstNewDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); + + using var _ = ArrayBuilder.GetInstance(out var unmergedChanges); + for (int i = 1, n = newDocumentsAndHashes.Count; i < n; i++) { - appliedChanges = await AddDocumentMergeChangesAsync( - oldSolution.GetDocument(documentId), - newSolution.GetDocument(documentId), - [.. appliedChanges], + var siblingNewDocument = newDocumentsAndHashes[i].newDocument; + var siblingOldDocument = oldSolution.GetRequiredDocument(siblingNewDocument.Id); + + allTextChangesAcrossLinkedFiles = await AddDocumentMergeChangesAsync( + siblingOldDocument, + siblingNewDocument, + allTextChangesAcrossLinkedFiles, unmergedChanges, - groupSessionInfo, textDifferencingService, cancellationToken).ConfigureAwait(false); } - var originalDocument = oldSolution.GetDocument(linkedDocumentGroup.First()); - var originalSourceText = await originalDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - - // Add comments in source explaining diffs that could not be merged + var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); - IEnumerable allChanges; - IList mergeConflictResolutionSpan = []; + if (unmergedChanges.Count == 0) + return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allTextChangesAcrossLinkedFiles), []); - if (unmergedChanges.Any()) - { - mergeConflictHandler ??= oldSolution.GetDocument(linkedDocumentGroup.First()).GetLanguageService(); - var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(originalSourceText, unmergedChanges); + mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); + var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); - allChanges = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits, mergeConflictResolutionSpan, groupSessionInfo); - } - else - { - allChanges = appliedChanges; - } - - groupSessionInfo.LinkedDocuments = newSolution.GetDocumentIdsWithFilePath(originalDocument.FilePath).Length; - groupSessionInfo.DocumentsWithChanges = linkedDocumentGroup.Count(); - sessionInfo.LogLinkedFileResult(groupSessionInfo); - - return new LinkedFileMergeResult(allLinkedDocuments, originalSourceText.WithChanges(allChanges), mergeConflictResolutionSpan); + // Add comments in source explaining diffs that could not be merged + var (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(allTextChangesAcrossLinkedFiles, mergeConflictTextEdits); + return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpans); } private static async Task> AddDocumentMergeChangesAsync( Document oldDocument, Document newDocument, - List cumulativeChanges, - List unmergedChanges, - LinkedFileGroupSessionInfo groupSessionInfo, + ImmutableArray cumulativeChanges, + ArrayBuilder unmergedChanges, IDocumentTextDifferencingService textDiffService, CancellationToken cancellationToken) { - var unmergedDocumentChanges = new List(); - var successfullyMergedChanges = ArrayBuilder.GetInstance(); + using var _1 = ArrayBuilder.GetInstance(out var unmergedDocumentChanges); + using var _2 = ArrayBuilder.GetInstance(out var successfullyMergedChanges); var cumulativeChangeIndex = 0; - var textchanges = await textDiffService.GetTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - foreach (var change in textchanges) + var textChanges = await textDiffService.GetTextChangesAsync( + oldDocument, newDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); + foreach (var change in textChanges) { - while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start) + while (cumulativeChangeIndex < cumulativeChanges.Length && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start) { // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); cumulativeChangeIndex++; - - groupSessionInfo.IsolatedDiffs++; } - if (cumulativeChangeIndex < cumulativeChanges.Count) + if (cumulativeChangeIndex < cumulativeChanges.Length) { var cumulativeChange = cumulativeChanges[cumulativeChangeIndex]; if (!cumulativeChange.Span.IntersectsWith(change.Span)) { // The current change in consideration does not intersect with any existing change successfullyMergedChanges.Add(change); - - groupSessionInfo.IsolatedDiffs++; } else { @@ -160,24 +172,12 @@ private static async Task> AddDocumentMergeChangesAsy // The current change in consideration overlaps an existing change but // the changes are not identical. unmergedDocumentChanges.Add(change); - - groupSessionInfo.OverlappingDistinctDiffs++; - if (change.Span == cumulativeChange.Span) - { - groupSessionInfo.OverlappingDistinctDiffsWithSameSpan++; - if (change.NewText.Contains(cumulativeChange.NewText) || cumulativeChange.NewText.Contains(change.NewText)) - { - groupSessionInfo.OverlappingDistinctDiffsWithSameSpanAndSubstringRelation++; - } - } } else { // The current change in consideration is identical to an existing change successfullyMergedChanges.Add(change); cumulativeChangeIndex++; - - groupSessionInfo.IdenticalDiffs++; } } } @@ -185,108 +185,99 @@ private static async Task> AddDocumentMergeChangesAsy { // The current change in consideration does not intersect with any existing change successfullyMergedChanges.Add(change); - - groupSessionInfo.IsolatedDiffs++; } } - while (cumulativeChangeIndex < cumulativeChanges.Count) + while (cumulativeChangeIndex < cumulativeChanges.Length) { // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); cumulativeChangeIndex++; - groupSessionInfo.IsolatedDiffs++; } - if (unmergedDocumentChanges.Any()) + if (unmergedDocumentChanges.Count != 0) { unmergedChanges.Add(new UnmergedDocumentChanges( - unmergedDocumentChanges.AsEnumerable(), + unmergedDocumentChanges.ToImmutableAndClear(), oldDocument.Project.Name, oldDocument.Id)); } - return successfullyMergedChanges.ToImmutableAndFree(); + return successfullyMergedChanges.ToImmutableAndClear(); } - private static IEnumerable MergeChangesWithMergeFailComments( - IEnumerable mergedChanges, - IEnumerable commentChanges, - IList mergeConflictResolutionSpans, - LinkedFileGroupSessionInfo groupSessionInfo) + private static (ImmutableArray mergeChanges, ImmutableArray mergeConflictResolutionSpans) MergeChangesWithMergeFailComments( + ImmutableArray mergedChanges, + ImmutableArray commentChanges) { - var mergedChangesList = NormalizeChanges(mergedChanges).ToList(); - var commentChangesList = NormalizeChanges(commentChanges).ToList(); + var mergedChangesList = NormalizeChanges(mergedChanges); + var commentChangesList = NormalizeChanges(commentChanges); - var combinedChanges = new List(); - var insertedMergeConflictCommentsAtAdjustedLocation = 0; + using var _1 = ArrayBuilder.GetInstance(out var combinedChanges); + using var _2 = ArrayBuilder.GetInstance(out var mergeConflictResolutionSpans); + var insertedMergeConflictCommentsAtAdjustedLocation = 0; var commentChangeIndex = 0; var currentPositionDelta = 0; foreach (var mergedChange in mergedChangesList) { - while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.End <= mergedChange.Span.Start) + while (commentChangeIndex < commentChangesList.Length && commentChangesList[commentChangeIndex].Span.End <= mergedChange.Span.Start) { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); - currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); + currentPositionDelta += commentChangesList[commentChangeIndex].NewText!.Length - commentChangesList[commentChangeIndex].Span.Length; commentChangeIndex++; } - if (commentChangeIndex >= commentChangesList.Count || mergedChange.Span.End <= commentChangesList[commentChangeIndex].Span.Start) + if (commentChangeIndex >= commentChangesList.Length || mergedChange.Span.End <= commentChangesList[commentChangeIndex].Span.Start) { // Add a merge change that does not conflict with any comment change combinedChanges.Add(mergedChange); - currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length); + currentPositionDelta += mergedChange.NewText!.Length - mergedChange.Span.Length; continue; } // The current comment insertion location conflicts with a merge diff location. Add the comment before the diff. var conflictingCommentInsertionLocation = new TextSpan(mergedChange.Span.Start, 0); - while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End) + while (commentChangeIndex < commentChangesList.Length && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End) { - combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText)); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); - currentPositionDelta += commentChangesList[commentChangeIndex].NewText.Length; + combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText!)); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); + currentPositionDelta += commentChangesList[commentChangeIndex].NewText!.Length; commentChangeIndex++; insertedMergeConflictCommentsAtAdjustedLocation++; } combinedChanges.Add(mergedChange); - currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length); + currentPositionDelta += mergedChange.NewText!.Length - mergedChange.Span.Length; } - while (commentChangeIndex < commentChangesList.Count) + while (commentChangeIndex < commentChangesList.Length) { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); - currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length); + currentPositionDelta += commentChangesList[commentChangeIndex].NewText!.Length - commentChangesList[commentChangeIndex].Span.Length; commentChangeIndex++; } - groupSessionInfo.InsertedMergeConflictComments = commentChanges.Count(); - groupSessionInfo.InsertedMergeConflictCommentsAtAdjustedLocation = insertedMergeConflictCommentsAtAdjustedLocation; - - return NormalizeChanges(combinedChanges); + return (NormalizeChanges(combinedChanges.ToImmutableAndClear()), mergeConflictResolutionSpans.ToImmutableAndClear()); } - private static IEnumerable NormalizeChanges(IEnumerable changes) + private static ImmutableArray NormalizeChanges(ImmutableArray changes) { - if (changes.Count() <= 1) - { + if (changes.Length <= 1) return changes; - } - changes = changes.OrderBy(c => c.Span.Start); - var normalizedChanges = new List(); + var orderedChanges = changes.Sort(static (c1, c2) => c1.Span.Start - c2.Span.Start); + using var _ = ArrayBuilder.GetInstance(changes.Length, out var normalizedChanges); - var currentChange = changes.First(); - foreach (var nextChange in changes.Skip(1)) + var currentChange = changes[0]; + foreach (var nextChange in changes.AsSpan()[1..]) { if (nextChange.Span.Start == currentChange.Span.End) { @@ -300,27 +291,11 @@ private static IEnumerable NormalizeChanges(IEnumerable } normalizedChanges.Add(currentChange); - return normalizedChanges; - } - - internal class LinkedFileDiffMergingSessionInfo - { - public readonly List LinkedFileGroups = []; - public void LogLinkedFileResult(LinkedFileGroupSessionInfo info) - => LinkedFileGroups.Add(info); - } + // If we didn't merge anything, can just return the original ordered changes. + if (normalizedChanges.Count == orderedChanges.Length) + return orderedChanges; - internal class LinkedFileGroupSessionInfo - { - public int LinkedDocuments; - public int DocumentsWithChanges; - public int IsolatedDiffs; - public int IdenticalDiffs; - public int OverlappingDistinctDiffs; - public int OverlappingDistinctDiffsWithSameSpan; - public int OverlappingDistinctDiffsWithSameSpanAndSubstringRelation; - public int InsertedMergeConflictComments; - public int InsertedMergeConflictCommentsAtAdjustedLocation; + return normalizedChanges.ToImmutableAndClear(); } } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs index a6a2fdea585d9..daf2558de6b42 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs @@ -2,18 +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. -#nullable disable - -using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; -internal sealed class LinkedFileMergeResult(IEnumerable documentIds, SourceText mergedSourceText, IEnumerable mergeConflictResolutionSpans) +internal readonly struct LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, ImmutableArray mergeConflictResolutionSpans) { - public IEnumerable DocumentIds { get; internal set; } = documentIds; - public SourceText MergedSourceText { get; internal set; } = mergedSourceText; - public IEnumerable MergeConflictResolutionSpans { get; } = mergeConflictResolutionSpans; - public bool HasMergeConflicts { get { return MergeConflictResolutionSpans.Any(); } } + public readonly ImmutableArray DocumentIds = documentIds; + public readonly SourceText MergedSourceText = mergedSourceText; + public readonly ImmutableArray MergeConflictResolutionSpans = mergeConflictResolutionSpans; + public bool HasMergeConflicts => !MergeConflictResolutionSpans.IsEmpty; } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index fd532034cbb68..0d3f426e966a6 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -2,9 +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 System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; @@ -13,19 +13,16 @@ internal sealed class LinkedFileMergeSessionResult { public Solution MergedSolution { get; } - private readonly Dictionary> _mergeConflictCommentSpans = []; - public Dictionary> MergeConflictCommentSpans => _mergeConflictCommentSpans; + public readonly Dictionary> MergeConflictCommentSpans = []; - public LinkedFileMergeSessionResult(Solution mergedSolution, IEnumerable fileMergeResults) + public LinkedFileMergeSessionResult(Solution mergedSolution, ArrayBuilder fileMergeResults) { this.MergedSolution = mergedSolution; foreach (var fileMergeResult in fileMergeResults) { foreach (var documentId in fileMergeResult.DocumentIds) - { - _mergeConflictCommentSpans.Add(documentId, fileMergeResult.MergeConflictResolutionSpans); - } + MergeConflictCommentSpans.Add(documentId, fileMergeResult.MergeConflictResolutionSpans); } } } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index a5586170c71fa..d5f463427e08d 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -2,16 +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.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; -internal sealed class UnmergedDocumentChanges(IEnumerable unmergedChanges, string projectName, DocumentId documentId) +internal readonly struct UnmergedDocumentChanges(ImmutableArray unmergedChanges, string projectName, DocumentId documentId) { - public IEnumerable UnmergedChanges { get; } = unmergedChanges; - public string ProjectName { get; } = projectName; - public DocumentId DocumentId { get; } = documentId; + public readonly ImmutableArray UnmergedChanges = unmergedChanges; + public readonly string ProjectName = projectName; + public readonly DocumentId DocumentId = documentId; } diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 2ef8b99744fab..39c411bd46163 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs index 4f8bb3b9ddc17..fc5b311d973ef 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs @@ -20,18 +20,7 @@ namespace Microsoft.CodeAnalysis.Remote; /// internal abstract class RemoteHostClient : IDisposable { - public event EventHandler? StatusChanged; - - protected void Started() - { - OnStatusChanged(started: true); - } - - public virtual void Dispose() - => OnStatusChanged(started: false); - - private void OnStatusChanged(bool started) - => StatusChanged?.Invoke(this, started); + public abstract void Dispose(); public static Task TryGetClientAsync(Project project, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs index 31746fb54306c..ee221fb20114f 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs @@ -47,15 +47,21 @@ internal static class RemoteUtilities /// a solution textually equivalent to the newSolution passed to . /// public static async Task UpdateSolutionAsync( - Solution oldSolution, ImmutableArray<(DocumentId, ImmutableArray)> documentTextChanges, CancellationToken cancellationToken) + Solution oldSolution, + ImmutableArray<(DocumentId documentId, ImmutableArray textChanges)> documentTextChanges, + CancellationToken cancellationToken) { var currentSolution = oldSolution; - foreach (var (docId, textChanges) in documentTextChanges) - { - var text = await oldSolution.GetDocument(docId).GetValueTextAsync(cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentText(docId, text.WithChanges(textChanges)); - } - return currentSolution; + var documentIdsAndTexts = await documentTextChanges + .SelectAsArrayAsync(async (tuple, cancellationToken) => + { + var oldText = await oldSolution.GetDocument(tuple.documentId).GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var newText = oldText.WithChanges(tuple.textChanges); + return (tuple.documentId, newText); + }, cancellationToken) + .ConfigureAwait(false); + + return oldSolution.WithDocumentTexts(documentIdsAndTexts); } } diff --git a/src/Workspaces/Core/Portable/Serialization/SerializedMetadataReference.cs b/src/Workspaces/Core/Portable/Serialization/SerializedMetadataReference.cs new file mode 100644 index 0000000000000..230bb0ddec317 --- /dev/null +++ b/src/Workspaces/Core/Portable/Serialization/SerializedMetadataReference.cs @@ -0,0 +1,85 @@ +// 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.Host; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Serialization; + +using static TemporaryStorageService; + +internal partial class SerializerService +{ + [DebuggerDisplay("{" + nameof(Display) + ",nq}")] + private sealed class SerializedMetadataReference : PortableExecutableReference, ISupportTemporaryStorage + { + private readonly Metadata _metadata; + private readonly ImmutableArray _storageHandles; + private readonly DocumentationProvider _provider; + + public IReadOnlyList StorageHandles => _storageHandles; + + public SerializedMetadataReference( + MetadataReferenceProperties properties, + string? fullPath, + Metadata metadata, + ImmutableArray storageHandles, + DocumentationProvider initialDocumentation) + : base(properties, fullPath, initialDocumentation) + { + Contract.ThrowIfTrue(storageHandles.IsDefault); + _metadata = metadata; + _storageHandles = storageHandles; + + _provider = initialDocumentation; + } + + protected override DocumentationProvider CreateDocumentationProvider() + { + // this uses documentation provider given at the constructor + throw ExceptionUtilities.Unreachable(); + } + + protected override Metadata GetMetadataImpl() + => _metadata; + + protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties) + => new SerializedMetadataReference(properties, FilePath, _metadata, _storageHandles, _provider); + + public override string ToString() + { + var metadata = TryGetMetadata(this); + var modules = GetModules(metadata); + + return $""" + {nameof(SerializedMetadataReference)} + FilePath={this.FilePath} + Kind={this.Properties.Kind} + Aliases={this.Properties.Aliases.Join(",")} + EmbedInteropTypes={this.Properties.EmbedInteropTypes} + MetadataKind={metadata switch { null => "null", AssemblyMetadata => "assembly", ModuleMetadata => "module", _ => metadata.GetType().Name }} + Guids={modules.Select(m => GetMetadataGuid(m).ToString()).Join(",")} + """; + + static ImmutableArray GetModules(Metadata? metadata) + { + if (metadata is AssemblyMetadata assemblyMetadata) + { + if (TryGetModules(assemblyMetadata, out var modules)) + return modules; + } + else if (metadata is ModuleMetadata moduleMetadata) + { + return [moduleMetadata]; + } + + return []; + } + } + } +} diff --git a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs index 67102657d9132..5e2f922c5ed2b 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs @@ -5,9 +5,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Reflection.Metadata; using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; @@ -23,6 +22,28 @@ internal partial class SerializerService { private const int MetadataFailed = int.MaxValue; + /// + /// Allow analyzer tests to exercise the oop codepaths, even though they're referring to in-memory instances of + /// DiagnosticAnalyzers. In that case, we'll just share the in-memory instance of the analyzer across the OOP + /// boundary (which still runs in proc in tests), but we will still exercise all codepaths that use the RemoteClient + /// as well as exercising all codepaths that send data across the OOP boundary. Effectively, this allows us to + /// pretend that a is a during tests. + /// + private static readonly object s_analyzerImageReferenceMapGate = new(); + private static IBidirectionalMap s_analyzerImageReferenceMap = BidirectionalMap.Empty; + + private static bool TryGetAnalyzerImageReferenceGuid(AnalyzerImageReference imageReference, out Guid guid) + { + lock (s_analyzerImageReferenceMapGate) + return s_analyzerImageReferenceMap.TryGetValue(imageReference, out guid); + } + + private static bool TryGetAnalyzerImageReferenceFromGuid(Guid guid, [NotNullWhen(true)] out AnalyzerImageReference? imageReference) + { + lock (s_analyzerImageReferenceMapGate) + return s_analyzerImageReferenceMap.TryGetKey(guid, out imageReference); + } + public static Checksum CreateChecksum(MetadataReference reference, CancellationToken cancellationToken) { if (reference is PortableExecutableReference portable) @@ -51,6 +72,11 @@ public static Checksum CreateChecksum(AnalyzerReference reference, CancellationT writer.WriteBoolean(IsAnalyzerReferenceWithShadowCopyLoader(file)); break; + case AnalyzerImageReference analyzerImageReference: + Contract.ThrowIfFalse(TryGetAnalyzerImageReferenceGuid(analyzerImageReference, out var guid), "AnalyzerImageReferences are only supported during testing"); + writer.WriteGuid(guid); + break; + default: throw ExceptionUtilities.UnexpectedValue(reference); } @@ -101,6 +127,12 @@ public virtual void WriteAnalyzerReferenceTo(AnalyzerReference reference, Object writer.WriteBoolean(IsAnalyzerReferenceWithShadowCopyLoader(file)); break; + case AnalyzerImageReference analyzerImageReference: + Contract.ThrowIfFalse(TryGetAnalyzerImageReferenceGuid(analyzerImageReference, out var guid), "AnalyzerImageReferences are only supported during testing"); + writer.WriteString(nameof(AnalyzerImageReference)); + writer.WriteGuid(guid); + break; + default: throw ExceptionUtilities.UnexpectedValue(reference); } @@ -111,11 +143,17 @@ public virtual AnalyzerReference ReadAnalyzerReferenceFrom(ObjectReader reader, cancellationToken.ThrowIfCancellationRequested(); var type = reader.ReadString(); - if (type == nameof(AnalyzerFileReference)) + switch (type) { - var fullPath = reader.ReadRequiredString(); - var shadowCopy = reader.ReadBoolean(); - return new AnalyzerFileReference(fullPath, _analyzerLoaderProvider.GetLoader(new AnalyzerAssemblyLoaderOptions(shadowCopy))); + case nameof(AnalyzerFileReference): + var fullPath = reader.ReadRequiredString(); + var shadowCopy = reader.ReadBoolean(); + return new AnalyzerFileReference(fullPath, _analyzerLoaderProvider.GetLoader(new AnalyzerAssemblyLoaderOptions(shadowCopy))); + + case nameof(AnalyzerImageReference): + var guid = reader.ReadGuid(); + Contract.ThrowIfFalse(TryGetAnalyzerImageReferenceFromGuid(guid, out var analyzerImageReference)); + return analyzerImageReference; } throw ExceptionUtilities.UnexpectedValue(type); @@ -503,70 +541,14 @@ protected override PortableExecutableReference WithPropertiesImpl(MetadataRefere => new MissingMetadataReference(properties, FilePath, _provider); } - [DebuggerDisplay("{" + nameof(Display) + ",nq}")] - private sealed class SerializedMetadataReference : PortableExecutableReference, ISupportTemporaryStorage + public static class TestAccessor { - private readonly Metadata _metadata; - private readonly ImmutableArray _storageHandles; - private readonly DocumentationProvider _provider; - - public IReadOnlyList StorageHandles => _storageHandles; - - public SerializedMetadataReference( - MetadataReferenceProperties properties, - string? fullPath, - Metadata metadata, - ImmutableArray storageHandles, - DocumentationProvider initialDocumentation) - : base(properties, fullPath, initialDocumentation) - { - Contract.ThrowIfTrue(storageHandles.IsDefault); - _metadata = metadata; - _storageHandles = storageHandles; - - _provider = initialDocumentation; - } - - protected override DocumentationProvider CreateDocumentationProvider() - { - // this uses documentation provider given at the constructor - throw ExceptionUtilities.Unreachable(); - } - - protected override Metadata GetMetadataImpl() - => _metadata; - - protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties) - => new SerializedMetadataReference(properties, FilePath, _metadata, _storageHandles, _provider); - - public override string ToString() + public static void AddAnalyzerImageReference(AnalyzerImageReference analyzerImageReference) { - var metadata = TryGetMetadata(this); - var modules = GetModules(metadata); - - return $""" - {nameof(SerializedMetadataReference)} - FilePath={this.FilePath} - Kind={this.Properties.Kind} - Aliases={this.Properties.Aliases.Join(",")} - EmbedInteropTypes={this.Properties.EmbedInteropTypes} - MetadataKind={metadata switch { null => "null", AssemblyMetadata => "assembly", ModuleMetadata => "module", _ => metadata.GetType().Name }} - Guids={modules.Select(m => GetMetadataGuid(m).ToString()).Join(",")} - """; - - static ImmutableArray GetModules(Metadata? metadata) + lock (s_analyzerImageReferenceMapGate) { - if (metadata is AssemblyMetadata assemblyMetadata) - { - if (TryGetModules(assemblyMetadata, out var modules)) - return modules; - } - else if (metadata is ModuleMetadata moduleMetadata) - { - return [moduleMetadata]; - } - - return []; + if (!s_analyzerImageReferenceMap.ContainsKey(analyzerImageReference)) + s_analyzerImageReferenceMap = s_analyzerImageReferenceMap.Add(analyzerImageReference, Guid.NewGuid()); } } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index aba239ec1cb92..f9ae9b7f0b3fd 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -658,11 +658,10 @@ public static ImmutableArray FilterToVisibleAndBrowsableSymbols( // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first // check to see if we're referencing a symbol defined in source. - static bool isSymbolDefinedInSource(Location l) => l.IsInSource; return symbols.WhereAsArray((s, arg) => // Check if symbol is namespace (which is always visible) first to avoid realizing all locations // of each namespace symbol, which might end up allocating in LOH - (s.IsNamespace() || s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && + (s.IsNamespace() || s.Locations.Any(static loc => loc.IsInSource) || !s.HasUnsupportedMetadata) && !s.IsDestructor() && s.IsEditorBrowsable( arg.hideAdvancedMembers, diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 47d9178365398..1eef6e5b28e97 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -17,15 +17,20 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class SourceTextExtensions { - // char pooled memory : 8K * 256 = 2MB + private const int ObjectPoolCount = 1024; + + // char array length: 4k characters. 4K * 1024 (object pool count) * 2 (bytes per char) = 8MB private const int CharArrayLength = 4 * 1024; + // 32k characters. Equivalent to 64KB in memory bytes. Will not be put into the LOH. + public const int SourceTextLengthThreshold = 32 * 1024; + /// /// Note: there is a strong invariant that you only get arrays back from this that are exactly long. Putting arrays back into this of the wrong length will result in broken /// behavior. Do not expose this pool outside of this class. /// - private static readonly ObjectPool s_charArrayPool = new(() => new char[CharArrayLength], 256); + private static readonly ObjectPool s_charArrayPool = new(() => new char[CharArrayLength], ObjectPoolCount); public static void GetLineAndOffset(this SourceText text, int position, out int lineNumber, out int offset) { @@ -168,9 +173,6 @@ public static int IndexOfNonWhiteSpace(this SourceText text, int start, int leng return -1; } - // 32KB. comes from SourceText char buffer size and less than large object size - internal const int SourceTextLengthThreshold = 32 * 1024 / sizeof(char); - public static void WriteTo(this SourceText sourceText, ObjectWriter writer, CancellationToken cancellationToken) { // Source length @@ -229,20 +231,25 @@ private static void WriteChunksTo(SourceText sourceText, ObjectWriter writer, in public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { - using var textReader = ObjectReaderTextReader.Create(reader); + using var textReader = CharArrayChunkTextReader.CreateFromObjectReader(reader); return textService.CreateText(textReader, encoding, checksumAlgorithm, cancellationToken); } - private sealed class ObjectReaderTextReader : TextReaderWithLength + private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int length) : TextReaderWithLength(length) { - private readonly ImmutableArray _chunks; - private readonly int _chunkSize; - private bool _disposed; + private readonly ImmutableArray _chunks = chunks; + private bool _disposed = false; - private int _position; + /// + /// Public so that the caller can assert that the new SourceText read all the way to the end of this successfully. + /// + public int Position { get; private set; } - public static TextReader Create(ObjectReader reader) + private static int GetIndexFromPosition(int position) => position / CharArrayLength; + private static int GetColumnFromPosition(int position) => position % CharArrayLength; + + public static TextReader CreateFromObjectReader(ObjectReader reader) { var length = reader.ReadInt32(); if (length < SourceTextLengthThreshold) @@ -256,7 +263,7 @@ public static TextReader Create(ObjectReader reader) var numberOfChunks = reader.ReadInt32(); // read as chunks - using var _ = ArrayBuilder.GetInstance(numberOfChunks, out var chunks); + var chunks = new FixedSizeArrayBuilder(numberOfChunks); var offset = 0; for (var i = 0; i < numberOfChunks; i++) @@ -279,17 +286,11 @@ public static TextReader Create(ObjectReader reader) } Contract.ThrowIfFalse(offset == length); - return new ObjectReaderTextReader(chunks.ToImmutableAndClear(), chunkSize, length); - } - private ObjectReaderTextReader(ImmutableArray chunks, int chunkSize, int length) - : base(length) - { - _chunks = chunks; - _chunkSize = chunkSize; - _disposed = false; - Contract.ThrowIfTrue(chunkSize != CharArrayLength); - Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, chunkSize)); + var chunksArray = chunks.MoveToImmutable(); + Contract.ThrowIfTrue(chunksArray.Any(static (c, s) => c.Length != s, CharArrayLength)); + + return new CharArrayChunkTextReader(chunksArray, length); } protected override void Dispose(bool disposing) @@ -308,53 +309,49 @@ protected override void Dispose(bool disposing) public override int Peek() { - if (_position >= Length) - { + if (Position >= Length) return -1; - } - return Read(_position); + return Read(Position); } public override int Read() { - if (_position >= Length) - { + if (Position >= Length) return -1; - } - return Read(_position++); + return Read(Position++); + } + + private int Read(int position) + { + var chunkIndex = GetIndexFromPosition(position); + var chunkColumn = GetColumnFromPosition(position); + + return _chunks[chunkIndex][chunkColumn]; } public override int Read(char[] buffer, int index, int count) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer)); - } if (index < 0 || index >= buffer.Length) - { throw new ArgumentOutOfRangeException(nameof(index)); - } if (count < 0 || (index + count) > buffer.Length) - { throw new ArgumentOutOfRangeException(nameof(count)); - } // check quick bail out if (count == 0) - { return 0; - } // adjust to actual char to read - var totalCharsToRead = Math.Min(count, Length - _position); + var totalCharsToRead = Math.Min(count, Length - Position); count = totalCharsToRead; - var chunkIndex = GetIndexFromPosition(_position); - var chunkStartOffset = GetColumnFromPosition(_position); + var chunkIndex = GetIndexFromPosition(Position); + var chunkStartOffset = GetColumnFromPosition(Position); while (true) { @@ -374,19 +371,9 @@ public override int Read(char[] buffer, int index, int count) chunkIndex++; } - _position += totalCharsToRead; + Position += totalCharsToRead; + Contract.ThrowIfTrue(Position > Length); return totalCharsToRead; } - - private int Read(int position) - { - var chunkIndex = GetIndexFromPosition(position); - var chunkColumn = GetColumnFromPosition(position); - - return _chunks[chunkIndex][chunkColumn]; - } - - private int GetIndexFromPosition(int position) => position / _chunkSize; - private int GetColumnFromPosition(int position) => position % _chunkSize; } } diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index 3a3bf94dfb4a5..2ad302dcb1782 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -10,18 +10,20 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Simplification; -internal abstract class AbstractSimplificationService : ISimplificationService +internal abstract class AbstractSimplificationService : ISimplificationService + where TCompilationUnitSyntax : SyntaxNode where TExpressionSyntax : SyntaxNode where TStatementSyntax : SyntaxNode where TCrefSyntax : SyntaxNode @@ -37,6 +39,7 @@ protected AbstractSimplificationService(ImmutableArray reducers protected abstract ImmutableArray GetNodesAndTokensToReduce(SyntaxNode root, Func isNodeOrTokenOutsideSimplifySpans); protected abstract SemanticModel GetSpeculativeSemanticModel(ref SyntaxNode nodeToSpeculate, SemanticModel originalSemanticModel, SyntaxNode originalNode); protected abstract bool NodeRequiresNonSpeculativeSemanticModel(SyntaxNode node); + protected abstract void AddImportDeclarations(TCompilationUnitSyntax root, ArrayBuilder importDeclarations); public abstract SimplifierOptions DefaultOptions { get; } public abstract SimplifierOptions GetSimplifierOptions(IOptionsReader options, SimplifierOptions? fallbackOptions); @@ -102,27 +105,22 @@ private async Task ReduceCoreAsync( // Create a simple interval tree for simplification spans. var spansTree = new TextSpanIntervalTree(spans); - bool isNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) - => !spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length); - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // prep namespace imports marked for simplification var removeIfUnusedAnnotation = new SyntaxAnnotation(); var originalRoot = root; - root = PrepareNamespaceImportsForRemovalIfUnused(document, root, removeIfUnusedAnnotation, isNodeOrTokenOutsideSimplifySpans); + root = PrepareNamespaceImportsForRemovalIfUnused(root, removeIfUnusedAnnotation, IsNodeOrTokenOutsideSimplifySpans); var hasImportsToSimplify = root != originalRoot; if (hasImportsToSimplify) { document = document.WithSyntaxRoot(root); - semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); } // Get the list of syntax nodes and tokens that need to be reduced. - var nodesAndTokensToReduce = this.GetNodesAndTokensToReduce(root, isNodeOrTokenOutsideSimplifySpans); + var nodesAndTokensToReduce = this.GetNodesAndTokensToReduce(root, IsNodeOrTokenOutsideSimplifySpans); if (nodesAndTokensToReduce.Any()) { @@ -142,7 +140,7 @@ bool isNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) // Reduce all the nodesAndTokensToReduce using the given reducers/rewriters and // store the reduced nodes and/or tokens in the reduced nodes/tokens maps. // Note that this method doesn't update the original syntax tree. - await this.ReduceAsync(document, root, nodesAndTokensToReduce, reducers, options, semanticModel, reducedNodesMap, reducedTokensMap, cancellationToken).ConfigureAwait(false); + await this.ReduceAsync(document, root, nodesAndTokensToReduce, reducers, options, reducedNodesMap, reducedTokensMap, cancellationToken).ConfigureAwait(false); if (reducedNodesMap.Any() || reducedTokensMap.Any()) { @@ -166,6 +164,9 @@ bool isNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) } return document; + + bool IsNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) + => !spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length); } private async Task ReduceAsync( @@ -174,7 +175,6 @@ private async Task ReduceAsync( ImmutableArray nodesAndTokensToReduce, ImmutableArray reducers, SimplifierOptions options, - SemanticModel semanticModel, ConcurrentDictionary reducedNodesMap, ConcurrentDictionary reducedTokensMap, CancellationToken cancellationToken) @@ -184,120 +184,125 @@ private async Task ReduceAsync( Contract.ThrowIfFalse(nodesAndTokensToReduce.Any()); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // Reduce each node or token in the given list by running it through each reducer. - var simplifyTasks = new Task[nodesAndTokensToReduce.Length]; - for (var i = 0; i < nodesAndTokensToReduce.Length; i++) + if (executeSerially) { - var nodeOrTokenToReduce = nodesAndTokensToReduce[i]; - simplifyTasks[i] = Task.Run(async () => - { - var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; - var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; - var semanticModelForReduce = semanticModel; - var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; - var isNode = nodeOrToken.IsNode; + foreach (var nodeOrTokenToReduce in nodesAndTokensToReduce) + await ReduceOneNodeOrTokenAsync(nodeOrTokenToReduce, cancellationToken).ConfigureAwait(false); + } + else + { + await RoslynParallel.ForEachAsync( + source: nodesAndTokensToReduce, + cancellationToken, + ReduceOneNodeOrTokenAsync).ConfigureAwait(false); + } - foreach (var reducer in reducers) - { - cancellationToken.ThrowIfCancellationRequested(); + return; + + async ValueTask ReduceOneNodeOrTokenAsync( + NodeOrTokenToReduce nodeOrTokenToReduce, CancellationToken cancellationToken) + { + // Reduce each node or token in the given list by running it through each reducer. - using var rewriter = reducer.GetOrCreateRewriter(); - rewriter.Initialize(document.Project.ParseOptions, options, cancellationToken); + var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; + var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; + var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; + var semanticModelForReduce = semanticModel; + var isNode = nodeOrToken.IsNode; - do + foreach (var reducer in reducers) + { + cancellationToken.ThrowIfCancellationRequested(); + + using var rewriter = reducer.GetOrCreateRewriter(); + rewriter.Initialize(document.Project.ParseOptions, options, cancellationToken); + + do + { + if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) { - if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) + // currentNodeOrToken was simplified either by a previous reducer or + // a previous iteration of the current reducer. + // Create a speculative semantic model for the simplified node for semantic queries. + + // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. + // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. + if (currentNodeOrToken.Parent == null && + nodeOrToken.Parent != null && + (currentNodeOrToken.IsToken || currentNodeOrToken.AsNode() is TExpressionSyntax or TStatementSyntax or TCrefSyntax)) { - // currentNodeOrToken was simplified either by a previous reducer or - // a previous iteration of the current reducer. - // Create a speculative semantic model for the simplified node for semantic queries. - - // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. - // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. - if (currentNodeOrToken.Parent == null && - nodeOrToken.Parent != null && - (currentNodeOrToken.IsToken || - currentNodeOrToken.AsNode() is TExpressionSyntax || - currentNodeOrToken.AsNode() is TStatementSyntax || - currentNodeOrToken.AsNode() is TCrefSyntax)) - { - var annotation = new SyntaxAnnotation(); - currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); + var annotation = new SyntaxAnnotation(); + currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); - var replacedParent = isNode - ? nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode()!, currentNodeOrToken.AsNode()!) - : nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); + var replacedParent = isNode + ? nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode()!, currentNodeOrToken.AsNode()!) + : nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); - currentNodeOrToken = replacedParent - .ChildNodesAndTokens() - .Single(c => c.HasAnnotation(annotation)); - } + currentNodeOrToken = replacedParent + .ChildNodesAndTokens() + .Single(c => c.HasAnnotation(annotation)); + } - if (isNode) + if (isNode) + { + var currentNode = currentNodeOrToken.AsNode()!; + if (this.NodeRequiresNonSpeculativeSemanticModel(nodeOrToken.AsNode()!)) + { + // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel + var marker = new SyntaxAnnotation(); + var newRoot = root.ReplaceNode(nodeOrToken.AsNode()!, currentNode.WithAdditionalAnnotations(marker)); + var newDocument = document.WithSyntaxRoot(newRoot); + semanticModelForReduce = await newDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + currentNodeOrToken = newRoot.DescendantNodes().Single(c => c.HasAnnotation(marker)); + } + else { - var currentNode = currentNodeOrToken.AsNode()!; - if (this.NodeRequiresNonSpeculativeSemanticModel(nodeOrToken.AsNode()!)) - { - // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel - var marker = new SyntaxAnnotation(); - var newRoot = root.ReplaceNode(nodeOrToken.AsNode()!, currentNode.WithAdditionalAnnotations(marker)); - var newDocument = document.WithSyntaxRoot(newRoot); - semanticModelForReduce = await newDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - currentNodeOrToken = newRoot.DescendantNodes().Single(c => c.HasAnnotation(marker)); - } - else - { - // Create speculative semantic model for simplified node. - semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()!); - currentNodeOrToken = currentNode; - } + // Create speculative semantic model for simplified node. + semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()!); + currentNodeOrToken = currentNode; } } - - // Reduce the current node or token. - currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } - while (rewriter.HasMoreWork); + + // Reduce the current node or token. + currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } + while (rewriter.HasMoreWork); + } - // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. - if (currentNodeOrToken != nodeOrToken) + // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. + if (currentNodeOrToken != nodeOrToken) + { + if (isNode) { - if (isNode) - { - reducedNodesMap[nodeOrToken.AsNode()!] = currentNodeOrToken.AsNode()!; - } - else - { - reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); - } + reducedNodesMap[nodeOrToken.AsNode()!] = currentNodeOrToken.AsNode()!; } - }, cancellationToken); - - if (executeSerially) - await simplifyTasks[i].ConfigureAwait(false); + else + { + reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); + } + } } - - await Task.WhenAll(simplifyTasks).ConfigureAwait(false); } // find any namespace imports / using directives marked for simplification in the specified spans // and add removeIfUnused annotation - private static SyntaxNode PrepareNamespaceImportsForRemovalIfUnused( - Document document, - SyntaxNode root, + private TCompilationUnitSyntax PrepareNamespaceImportsForRemovalIfUnused( + TCompilationUnitSyntax root, SyntaxAnnotation removeIfUnusedAnnotation, Func isNodeOrTokenOutsideSimplifySpan) { - var gen = SyntaxGenerator.GetGenerator(document); + using var _ = ArrayBuilder.GetInstance(out var importDeclarations); - var importsToSimplify = root.DescendantNodes().Where(n => - !isNodeOrTokenOutsideSimplifySpan(n) - && gen.GetDeclarationKind(n) == DeclarationKind.NamespaceImport - && n.HasAnnotation(Simplifier.Annotation)); + this.AddImportDeclarations(root, importDeclarations); - return root.ReplaceNodes(importsToSimplify, (o, r) => r.WithAdditionalAnnotations(removeIfUnusedAnnotation)); + return root.ReplaceNodes( + importDeclarations.Where(n => !isNodeOrTokenOutsideSimplifySpan(n) && n.HasAnnotation(Simplifier.Annotation)), + (o, r) => r.WithAdditionalAnnotations(removeIfUnusedAnnotation)); } private async Task RemoveUnusedNamespaceImportsAsync( diff --git a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs index 33a083cc0f12f..2c5f5529dff5d 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -14,7 +15,7 @@ namespace Microsoft.CodeAnalysis.SymbolSearch; /// Service that allows you to query the SymbolSearch database and which keeps /// the database up to date. /// -internal interface ISymbolSearchUpdateEngine +internal interface ISymbolSearchUpdateEngine : IDisposable { ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs b/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs index 23aded8e6276a..b26b770f7dcf0 100644 --- a/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs +++ b/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs @@ -3,44 +3,26 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Telemetry; /// /// Provides access to posting telemetry events or adding information -/// to aggregated telemetry events. Posts pending telemetry at 30 -/// minute intervals. +/// to aggregated telemetry events. /// internal static class TelemetryLogging { private static ITelemetryLogProvider? s_logProvider; - private static AsyncBatchingWorkQueue? s_postTelemetryQueue; public const string KeyName = "Name"; public const string KeyValue = "Value"; public const string KeyLanguageName = "LanguageName"; public const string KeyMetricName = "MetricName"; - public static event EventHandler? Flushed; - - public static void SetLogProvider(ITelemetryLogProvider logProvider, IAsynchronousOperationListener asyncListener) + public static void SetLogProvider(ITelemetryLogProvider logProvider) { s_logProvider = logProvider; - - InterlockedOperations.Initialize(ref s_postTelemetryQueue, () => - new AsyncBatchingWorkQueue( - TimeSpan.FromMinutes(30), - PostCollectedTelemetryAsync, - asyncListener, - CancellationToken.None)); - - // Add the initial item to the queue to ensure later processing. - s_postTelemetryQueue?.AddWork(); } /// @@ -130,17 +112,5 @@ public static void LogAggregated(FunctionId functionId, KeyValueLogMessage logMe public static void Flush() { s_logProvider?.Flush(); - - Flushed?.Invoke(null, EventArgs.Empty); - } - - private static ValueTask PostCollectedTelemetryAsync(CancellationToken cancellationToken) - { - Flush(); - - // Ensure PostCollectedTelemetryAsync will get fired again after the collection period. - s_postTelemetryQueue?.AddWork(); - - return ValueTaskFactory.CompletedTask; } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs index 35f03538856e8..b5a0c1f372766 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs @@ -4,13 +4,10 @@ #nullable disable -using System; using System.Collections.Generic; -using System.IO; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs index fa08ca56e7319..4b1f14104ae9c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis; #pragma warning restore CA1200 // Avoid using cref tags with a prefix [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] [DataContract] -public sealed class ProjectId : IEquatable +public sealed class ProjectId : IEquatable, IComparable { /// /// Checksum of this ProjectId, built only from . @@ -37,8 +37,8 @@ public sealed class ProjectId : IEquatable /// /// An optional name to show only for debugger-display purposes. This must not be used for any other - /// purpose. Importantly, it must not be part of the equality/hashing contract of this type (including ). + /// purpose. Importantly, it must not be part of the equality/hashing/comparable contract of this type (including + /// ). /// [DataMember(Order = 1)] private readonly string? _debugName; @@ -114,4 +114,12 @@ internal Checksum Checksum writer.WriteString(nameof(ProjectId)); writer.WriteGuid(@this.Id); }), this); + + int IComparable.CompareTo(ProjectId? other) + { + if (other is null) + return 1; + + return this.Id.CompareTo(other.Id); + } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 013c0f9aaa830..10b51a65ae47c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -185,28 +185,35 @@ private static async Task ComputeLatestDocumentVersionAsync(TextDo } private AsyncLazy CreateLazyLatestDocumentTopLevelChangeVersion( - TextDocumentState newDocument, + ImmutableArray newDocuments, TextDocumentStates newDocumentStates, TextDocumentStates newAdditionalDocumentStates) { if (_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var oldVersion)) { - return AsyncLazy.Create(static (arg, c) => - ComputeTopLevelChangeTextVersionAsync(arg.oldVersion, arg.newDocument, c), - arg: (oldVersion, newDocument)); + return AsyncLazy.Create(static (arg, cancellationToken) => + ComputeTopLevelChangeTextVersionAsync(arg.oldVersion, arg.newDocuments, cancellationToken), + arg: (oldVersion, newDocuments)); } else { - return AsyncLazy.Create(static (arg, c) => - ComputeLatestDocumentTopLevelChangeVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, c), + return AsyncLazy.Create(static (arg, cancellationToken) => + ComputeLatestDocumentTopLevelChangeVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, cancellationToken), arg: (newDocumentStates, newAdditionalDocumentStates)); } } - private static async Task ComputeTopLevelChangeTextVersionAsync(VersionStamp oldVersion, TextDocumentState newDocument, CancellationToken cancellationToken) + private static async Task ComputeTopLevelChangeTextVersionAsync( + VersionStamp oldVersion, ImmutableArray newDocuments, CancellationToken cancellationToken) { - var newVersion = await newDocument.GetTopLevelChangeTextVersionAsync(cancellationToken).ConfigureAwait(false); - return newVersion.GetNewerVersion(oldVersion); + var finalVersion = oldVersion; + foreach (var newDocument in newDocuments) + { + var newVersion = await newDocument.GetTopLevelChangeTextVersionAsync(cancellationToken).ConfigureAwait(false); + finalVersion = newVersion.GetNewerVersion(finalVersion); + } + + return finalVersion; } private static async Task ComputeLatestDocumentTopLevelChangeVersionAsync(TextDocumentStates documentStates, TextDocumentStates additionalDocumentStates, CancellationToken cancellationToken) @@ -834,16 +841,25 @@ public ProjectState RemoveAllNormalDocuments() } public ProjectState UpdateDocument(DocumentState newDocument, bool contentChanged) + => UpdateDocuments([newDocument], contentChanged); + + public ProjectState UpdateDocuments(ImmutableArray newDocuments, bool contentChanged) { - var oldDocument = DocumentStates.GetRequiredState(newDocument.Id); - if (oldDocument == newDocument) - { + var oldDocuments = newDocuments.SelectAsArray(d => DocumentStates.GetRequiredState(d.Id)); + if (oldDocuments.SequenceEqual(newDocuments)) return this; - } - var newDocumentStates = DocumentStates.SetState(newDocument.Id, newDocument); + // Must not be empty as we would have otherwise bailed out in the check above. + Contract.ThrowIfTrue(newDocuments.IsEmpty); + + var newDocumentStates = DocumentStates.SetStates(newDocuments); + + // When computing the latest dependent version, we just need to know how GetLatestDependentVersions( - newDocumentStates, AdditionalDocumentStates, oldDocument, newDocument, contentChanged, + newDocumentStates, AdditionalDocumentStates, + oldDocuments.CastArray(), + newDocuments.CastArray(), + contentChanged, out var dependentDocumentVersion, out var dependentSemanticVersion); return With( @@ -860,9 +876,9 @@ public ProjectState UpdateAdditionalDocument(AdditionalDocumentState newDocument return this; } - var newDocumentStates = AdditionalDocumentStates.SetState(newDocument.Id, newDocument); + var newDocumentStates = AdditionalDocumentStates.SetState(newDocument); GetLatestDependentVersions( - DocumentStates, newDocumentStates, oldDocument, newDocument, contentChanged, + DocumentStates, newDocumentStates, [oldDocument], [newDocument], contentChanged, out var dependentDocumentVersion, out var dependentSemanticVersion); return this.With( @@ -879,7 +895,7 @@ public ProjectState UpdateAnalyzerConfigDocument(AnalyzerConfigDocumentState new return this; } - var newDocumentStates = AnalyzerConfigDocumentStates.SetState(newDocument.Id, newDocument); + var newDocumentStates = AnalyzerConfigDocumentStates.SetState(newDocument); return CreateNewStateForChangedAnalyzerConfigDocuments(newDocumentStates); } @@ -899,46 +915,71 @@ public ProjectState UpdateDocumentsOrder(ImmutableList documentIds) private void GetLatestDependentVersions( TextDocumentStates newDocumentStates, TextDocumentStates newAdditionalDocumentStates, - TextDocumentState oldDocument, TextDocumentState newDocument, + ImmutableArray oldDocuments, + ImmutableArray newDocuments, bool contentChanged, - out AsyncLazy dependentDocumentVersion, out AsyncLazy dependentSemanticVersion) + out AsyncLazy dependentDocumentVersion, + out AsyncLazy dependentSemanticVersion) { var recalculateDocumentVersion = false; var recalculateSemanticVersion = false; if (contentChanged) { - if (oldDocument.TryGetTextVersion(out var oldVersion)) + foreach (var oldDocument in oldDocuments) { - if (!_lazyLatestDocumentVersion.TryGetValue(out var documentVersion) || documentVersion == oldVersion) + if (oldDocument.TryGetTextVersion(out var oldVersion)) { - recalculateDocumentVersion = true; - } + if (!_lazyLatestDocumentVersion.TryGetValue(out var documentVersion) || documentVersion == oldVersion) + recalculateDocumentVersion = true; - if (!_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var semanticVersion) || semanticVersion == oldVersion) - { - recalculateSemanticVersion = true; + if (!_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var semanticVersion) || semanticVersion == oldVersion) + recalculateSemanticVersion = true; } + + if (recalculateDocumentVersion && recalculateSemanticVersion) + break; } } - dependentDocumentVersion = recalculateDocumentVersion - ? AsyncLazy.Create(static (arg, c) => - ComputeLatestDocumentVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, c), - arg: (newDocumentStates, newAdditionalDocumentStates)) - : contentChanged - ? AsyncLazy.Create(static (newDocument, c) => - newDocument.GetTextVersionAsync(c), - arg: newDocument) - : _lazyLatestDocumentVersion; - - dependentSemanticVersion = recalculateSemanticVersion - ? AsyncLazy.Create(static (arg, c) => - ComputeLatestDocumentTopLevelChangeVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, c), - arg: (newDocumentStates, newAdditionalDocumentStates)) - : contentChanged - ? CreateLazyLatestDocumentTopLevelChangeVersion(newDocument, newDocumentStates, newAdditionalDocumentStates) - : _lazyLatestDocumentTopLevelChangeVersion; + if (recalculateDocumentVersion) + { + dependentDocumentVersion = AsyncLazy.Create(static (arg, cancellationToken) => + ComputeLatestDocumentVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, cancellationToken), + arg: (newDocumentStates, newAdditionalDocumentStates)); + } + else if (contentChanged) + { + dependentDocumentVersion = AsyncLazy.Create( + static async (newDocuments, cancellationToken) => + { + var finalVersion = await newDocuments[0].GetTextVersionAsync(cancellationToken).ConfigureAwait(false); + for (var i = 1; i < newDocuments.Length; i++) + finalVersion = finalVersion.GetNewerVersion(await newDocuments[i].GetTextVersionAsync(cancellationToken).ConfigureAwait(false)); + + return finalVersion; + }, + arg: newDocuments); + } + else + { + dependentDocumentVersion = _lazyLatestDocumentVersion; + } + + if (recalculateSemanticVersion) + { + dependentSemanticVersion = AsyncLazy.Create(static (arg, cancellationToken) => + ComputeLatestDocumentTopLevelChangeVersionAsync(arg.newDocumentStates, arg.newAdditionalDocumentStates, cancellationToken), + arg: (newDocumentStates, newAdditionalDocumentStates)); + } + else if (contentChanged) + { + dependentSemanticVersion = CreateLazyLatestDocumentTopLevelChangeVersion(newDocuments, newDocumentStates, newAdditionalDocumentStates); + } + else + { + dependentSemanticVersion = _lazyLatestDocumentTopLevelChangeVersion; + } } public void AddDocumentIdsWithFilePath(ref TemporaryArray temporaryArray, string filePath) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index df13aee1dcd6a..978f704c4154c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1181,20 +1181,25 @@ public Solution WithDocumentFilePath(DocumentId documentId, string filePath) /// specified. /// public Solution WithDocumentText(DocumentId documentId, SourceText text, PreservationMode mode = PreservationMode.PreserveValue) - { - CheckContainsDocument(documentId); + => WithDocumentTexts([(documentId, text, mode)]); - if (text == null) - { - throw new ArgumentNullException(nameof(text)); - } + internal Solution WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text)> texts) + => WithDocumentTexts(texts.SelectAsArray(t => (t.documentId, t.text, PreservationMode.PreserveValue))); - if (!mode.IsValid()) + internal Solution WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) + { + foreach (var (documentId, text, mode) in texts) { - throw new ArgumentOutOfRangeException(nameof(mode)); + CheckContainsDocument(documentId); + + if (text == null) + throw new ArgumentNullException(nameof(text)); + + if (!mode.IsValid()) + throw new ArgumentOutOfRangeException(nameof(mode)); } - return WithCompilationState(_compilationState.WithDocumentText(documentId, text, mode)); + return WithCompilationState(_compilationState.WithDocumentTexts(texts)); } /// @@ -1307,20 +1312,27 @@ public Solution WithAnalyzerConfigDocumentText(DocumentId documentId, TextAndVer /// rooted by the specified syntax node. /// public Solution WithDocumentSyntaxRoot(DocumentId documentId, SyntaxNode root, PreservationMode mode = PreservationMode.PreserveValue) - { - CheckContainsDocument(documentId); + => WithDocumentSyntaxRoots([(documentId, root, mode)]); - if (root == null) - { - throw new ArgumentNullException(nameof(root)); - } + /// . + internal Solution WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root)> syntaxRoots) + => WithDocumentSyntaxRoots(syntaxRoots.SelectAsArray(t => (t.documentId, t.root, PreservationMode.PreserveValue))); - if (!mode.IsValid()) + /// . + internal Solution WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root, PreservationMode mode)> syntaxRoots) + { + foreach (var (documentId, root, mode) in syntaxRoots) { - throw new ArgumentOutOfRangeException(nameof(mode)); + CheckContainsDocument(documentId); + + if (root == null) + throw new ArgumentNullException(nameof(root)); + + if (!mode.IsValid()) + throw new ArgumentOutOfRangeException(nameof(mode)); } - return WithCompilationState(_compilationState.WithDocumentSyntaxRoot(documentId, root, mode)); + return WithCompilationState(_compilationState.WithDocumentSyntaxRoots(syntaxRoots)); } internal Solution WithDocumentContentsFrom(DocumentId documentId, DocumentState documentState, bool forceEvenIfTreesWouldDiffer) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index 4aea9e09b1162..3c9bd134dca24 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs @@ -144,7 +144,7 @@ private partial class CompilationTracker : ICompilationTracker foreach (var (documentIdentity, _, generationDateTime) in infos) { var documentId = documentIdentity.DocumentId; - oldGeneratedDocuments = oldGeneratedDocuments.SetState(documentId, oldGeneratedDocuments.GetRequiredState(documentId).WithGenerationDateTime(generationDateTime)); + oldGeneratedDocuments = oldGeneratedDocuments.SetState(oldGeneratedDocuments.GetRequiredState(documentId).WithGenerationDateTime(generationDateTime)); } // If there are no generated documents though, then just use the compilationWithoutGeneratedFiles so we diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs index d59b89fe1bcc9..da0ca13cc63d8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs @@ -162,7 +162,7 @@ public async ValueTask> GetSour { // The generated file still exists in the underlying compilation, but the contents may not match the open file if the open file // is stale. Replace the syntax tree so we have a tree that matches the text. - newStates = newStates.SetState(id, replacementState); + newStates = newStates.SetState(replacementState); } else { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 30961e91913ed..6d624d7c540f0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.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. -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -17,27 +17,34 @@ internal partial class SolutionCompilationState { private abstract partial class TranslationAction { - internal sealed class TouchDocumentAction( + internal sealed class TouchDocumentsAction( ProjectState oldProjectState, ProjectState newProjectState, - DocumentState oldState, - DocumentState newState) - : TranslationAction(oldProjectState, newProjectState) + ImmutableArray newStates) : TranslationAction(oldProjectState, newProjectState) { - private readonly DocumentState _oldState = oldState; - private readonly DocumentState _newState = newState; + private readonly ImmutableArray _oldStates = newStates.SelectAsArray(s => oldProjectState.DocumentStates.GetRequiredState(s.Id)); + private readonly ImmutableArray _newStates = newStates; public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { - return oldCompilation.ReplaceSyntaxTree( - await _oldState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false), - await _newState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); - } + var finalCompilation = oldCompilation; + for (int i = 0, n = _newStates.Length; i < n; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + var newState = _newStates[i]; + var oldState = _oldStates[i]; + finalCompilation = finalCompilation.ReplaceSyntaxTree( + await oldState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false), + await newState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); + } - public DocumentId DocumentId => _newState.Attributes.Id; + return finalCompilation; + } - // Replacing a single tree doesn't impact the generated trees in a compilation, so we can use this against - // compilations that have generated trees. + /// + /// Replacing a single tree doesn't impact the generated trees in a compilation, so we can use this against + /// compilations that have generated trees. + /// public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput => true; public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver) @@ -45,12 +52,12 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generat public override TranslationAction? TryMergeWithPrior(TranslationAction priorAction) { - if (priorAction is TouchDocumentAction priorTouchAction && - priorTouchAction._newState == _oldState) + if (priorAction is TouchDocumentsAction priorTouchAction && + priorTouchAction._newStates.SequenceEqual(_oldStates)) { // As we're merging ourselves with the prior touch action, we want to keep the old project state // that we are translating from. - return new TouchDocumentAction(priorAction.OldProjectState, this.NewProjectState, priorTouchAction._oldState, _newState); + return new TouchDocumentsAction(priorAction.OldProjectState, this.NewProjectState, _newStates); } return null; @@ -138,37 +145,23 @@ internal sealed class AddDocumentsAction( public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { -#if NETSTANDARD - using var _1 = ArrayBuilder.GetInstance(this.Documents.Length, out var tasks); - - // We want to parse in parallel. But we don't want to have too many parses going on at the same time. - // So we use a semaphore here to only allow that many in at a time. Once we hit that amount, it will - // block further parallel work. However, as the semaphore is released, new work will be let in. - var semaphore = new SemaphoreSlim(initialCount: AddDocumentsBatchSize); - foreach (var document in this.Documents) - { - tasks.Add(Task.Run(async () => - { - using (await semaphore.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - }, cancellationToken)); - } - - await Task.WhenAll(tasks).ConfigureAwait(false); -#else - await Parallel.ForEachAsync( - this.Documents, - cancellationToken, - static async (document, cancellationToken) => - await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); -#endif - - using var _2 = ArrayBuilder.GetInstance(this.Documents.Length, out var trees); - + // TODO(cyrusn): Do we need to ensure that the syntax trees we add to the compilation are in the same + // order as the documents array we have added to the project? If not, we can remove this map and the + // sorting below. + using var _ = PooledDictionary.GetInstance(out var documentToIndex); foreach (var document in this.Documents) - trees.Add(await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); - - return oldCompilation.AddSyntaxTrees(trees); + documentToIndex.Add(document, documentToIndex.Count); + + var documentsAndTrees = await ProducerConsumer<(DocumentState document, SyntaxTree tree)>.RunParallelAsync( + source: this.Documents, + produceItems: static async (document, callback, _, cancellationToken) => + callback((document, await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false))), + args: default(VoidResult), + cancellationToken).ConfigureAwait(false); + + return oldCompilation.AddSyntaxTrees(documentsAndTrees + .Sort((dt1, dt2) => documentToIndex[dt1.document] - documentToIndex[dt2.document]) + .Select(static dt => dt.tree)); } // This action adds the specified trees, but leaves the generated trees untouched. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 058055eb643fe..4bcb509819238 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -709,11 +709,50 @@ public SolutionCompilationState WithDocumentFilePath( } /// - public SolutionCompilationState WithDocumentText( - DocumentId documentId, SourceText text, PreservationMode mode) + public SolutionCompilationState WithDocumentText(DocumentId documentId, SourceText text, PreservationMode mode) + => WithDocumentTexts([(documentId, text, mode)]); + + internal SolutionCompilationState WithDocumentTexts( + ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) { - return UpdateDocumentState( - this.SolutionState.WithDocumentText(documentId, text, mode), documentId); + return WithDocumentContents( + texts, IsUnchanged, + static (documentState, text, mode) => documentState.UpdateText(text, mode)); + + static bool IsUnchanged(DocumentState oldDocument, SourceText text) + => oldDocument.TryGetText(out var oldText) && text == oldText; + } + + private SolutionCompilationState WithDocumentContents( + ImmutableArray<(DocumentId documentId, TContent content, PreservationMode mode)> texts, + Func isUnchanged, + Func updateContent) + { + return UpdateDocumentsInMultipleProjects( + texts.GroupBy(d => d.documentId.ProjectId).Select(g => + { + var projectId = g.Key; + var projectState = this.SolutionState.GetRequiredProjectState(projectId); + + using var _ = ArrayBuilder.GetInstance(out var newDocumentStates); + foreach (var (documentId, content, mode) in g) + { + var documentState = projectState.DocumentStates.GetRequiredState(documentId); + if (isUnchanged(documentState, content)) + continue; + + newDocumentStates.Add(updateContent(documentState, content, mode)); + } + + return (projectId, newDocumentStates.ToImmutableAndClear()); + }), + static (projectState, newDocumentStates) => + { + return new TranslationAction.TouchDocumentsAction( + projectState, + projectState.UpdateDocuments(newDocumentStates, contentChanged: true), + newDocumentStates); + }); } public SolutionCompilationState WithDocumentState( @@ -762,12 +801,19 @@ public SolutionCompilationState WithAnalyzerConfigDocumentText( this.SolutionState.WithAnalyzerConfigDocumentText(documentId, textAndVersion, mode)); } - /// - public SolutionCompilationState WithDocumentSyntaxRoot( - DocumentId documentId, SyntaxNode root, PreservationMode mode) + /// + public SolutionCompilationState WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root, PreservationMode mode)> syntaxRoots) { - return UpdateDocumentState( - this.SolutionState.WithDocumentSyntaxRoot(documentId, root, mode), documentId); + return WithDocumentContents( + syntaxRoots, IsUnchanged, + static (documentState, root, mode) => documentState.UpdateTree(root, mode)); + + static bool IsUnchanged(DocumentState oldDocument, SyntaxNode root) + { + return oldDocument.TryGetSyntaxTree(out var oldTree) && + oldTree.TryGetRoot(out var oldRoot) && + oldRoot == root; + } } public SolutionCompilationState WithDocumentContentsFrom( @@ -839,10 +885,10 @@ private SolutionCompilationState UpdateDocumentState(StateChange stateChange, Do // This function shouldn't have been called if the document has not changed Debug.Assert(stateChange.OldProjectState != stateChange.NewProjectState); - var oldDocument = stateChange.OldProjectState.DocumentStates.GetRequiredState(documentId); var newDocument = stateChange.NewProjectState.DocumentStates.GetRequiredState(documentId); - return new TranslationAction.TouchDocumentAction(stateChange.OldProjectState, stateChange.NewProjectState, oldDocument, newDocument); + return new TranslationAction.TouchDocumentsAction( + stateChange.OldProjectState, stateChange.NewProjectState, [newDocument]); }, forkTracker: true, arg: documentId); @@ -1408,7 +1454,7 @@ static SolutionCompilationState ComputeFrozenPartialState( } // Now, add all missing documents per project. - currentState = currentState.AddDocumentsToMultipleProjects( + currentState = currentState.UpdateDocumentsInMultipleProjects( // Do a SelectAsArray here to ensure that we realize the array once, and as such only call things like // ToImmutableAndFree once per ArrayBuilder. missingDocumentStates.SelectAsArray(kvp => (kvp.Key, kvp.Value.ToImmutableAndFree())), @@ -1482,7 +1528,7 @@ private SolutionCompilationState AddDocumentsToMultipleProjects( // The documents might be contributing to multiple different projects; split them by project and then we'll // process one project at a time. - return AddDocumentsToMultipleProjects( + return UpdateDocumentsInMultipleProjects( documentInfos.GroupBy(d => d.Id.ProjectId).Select(g => { var projectId = g.Key; @@ -1493,17 +1539,17 @@ private SolutionCompilationState AddDocumentsToMultipleProjects( addDocumentsToProjectState); } - private SolutionCompilationState AddDocumentsToMultipleProjects( - IEnumerable<(ProjectId projectId, ImmutableArray newDocumentStates)> projectIdAndNewDocuments, - Func, TranslationAction> addDocumentsToProjectState) + private SolutionCompilationState UpdateDocumentsInMultipleProjects( + IEnumerable<(ProjectId projectId, ImmutableArray updatedDocumentState)> projectIdAndUpdatedDocuments, + Func, TranslationAction> getTranslationAction) where TDocumentState : TextDocumentState { var newCompilationState = this; - foreach (var (projectId, newDocumentStates) in projectIdAndNewDocuments) + foreach (var (projectId, newDocumentStates) in projectIdAndUpdatedDocuments) { var oldProjectState = newCompilationState.SolutionState.GetRequiredProjectState(projectId); - var compilationTranslationAction = addDocumentsToProjectState(oldProjectState, newDocumentStates); + var compilationTranslationAction = getTranslationAction(oldProjectState, newDocumentStates); var newProjectState = compilationTranslationAction.NewProjectState; var stateChange = newCompilationState.SolutionState.ForkProject( diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 9600215792ec1..9e9663b46e49b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -988,24 +988,6 @@ public StateChange WithAnalyzerConfigDocumentText(DocumentId documentId, TextAnd return UpdateAnalyzerConfigDocumentState(oldDocument.UpdateText(textAndVersion, mode)); } - /// - /// Creates a new solution instance with the document specified updated to have a syntax tree - /// rooted by the specified syntax node. - /// - public StateChange WithDocumentSyntaxRoot(DocumentId documentId, SyntaxNode root, PreservationMode mode = PreservationMode.PreserveValue) - { - var oldDocument = GetRequiredDocumentState(documentId); - if (oldDocument.TryGetSyntaxTree(out var oldTree) && - oldTree.TryGetRoot(out var oldRoot) && - oldRoot == root) - { - var oldProject = GetRequiredProjectState(documentId.ProjectId); - return new(this, oldProject, oldProject); - } - - return UpdateDocumentState(oldDocument.UpdateTree(root, mode), contentChanged: true); - } - /// Whether or not the specified document is forced to have the same text and /// green-tree-root from . If , then they will share /// these values. If , then they will only be shared when safe to do so (for example, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs index 765e3cae91379..8b87aa885a8c3 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Collections; @@ -48,14 +49,14 @@ public static SourceGeneratorExecutionVersion ReadFrom(ObjectReader reader) /// Helper construct to allow a mapping from s to . /// Limited to just the surface area the workspace needs. /// -internal sealed class SourceGeneratorExecutionVersionMap(ImmutableSegmentedDictionary map) +internal sealed class SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary map) { public static readonly SourceGeneratorExecutionVersionMap Empty = new(); - public ImmutableSegmentedDictionary Map { get; } = map; + public ImmutableSortedDictionary Map { get; } = map; public SourceGeneratorExecutionVersionMap() - : this(ImmutableSegmentedDictionary.Empty) + : this(ImmutableSortedDictionary.Empty) { } @@ -75,6 +76,8 @@ public override bool Equals([NotNullWhen(true)] object? obj) public void WriteTo(ObjectWriter writer) { + // Writing out the dictionary in order is fine. That's because it's a sorted dictionary, and ProjectIds are + // naturally comparable. writer.WriteInt32(Map.Count); foreach (var (projectId, version) in Map) { @@ -86,7 +89,7 @@ public void WriteTo(ObjectWriter writer) public static SourceGeneratorExecutionVersionMap Deserialize(ObjectReader reader) { var count = reader.ReadInt32(); - var builder = ImmutableSegmentedDictionary.CreateBuilder(); + var builder = ImmutableSortedDictionary.CreateBuilder(); for (var i = 0; i < count; i++) { var projectId = ProjectId.ReadFrom(reader); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index d9a24fa7d891a..6a4fee023332c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -168,14 +168,27 @@ public TextDocumentStates RemoveRange(ImmutableArray ids) return new(_ids.RemoveRange(enumerableIds), _map.RemoveRange(enumerableIds), filePathToDocumentIds: null); } - internal TextDocumentStates SetState(DocumentId id, TState state) + internal TextDocumentStates SetState(TState state) + => SetStates([state]); + + internal TextDocumentStates SetStates(ImmutableArray states) { - var oldState = _map[id]; - var filePathToDocumentIds = oldState.FilePath != state.FilePath - ? null - : _filePathToDocumentIds; + var builder = _map.ToBuilder(); + var filePathToDocumentIds = _filePathToDocumentIds; + + foreach (var state in states) + { + var id = state.Id; + var oldState = _map[id]; + + // If any file paths have changed, don't preseve the computed map. We'll regenerate the new map on demand when needed. + if (filePathToDocumentIds != null && oldState.FilePath != state.FilePath) + filePathToDocumentIds = null; + + builder[id] = state; + } - return new(_ids, _map.SetItem(id, state), filePathToDocumentIds); + return new(_ids, builder.ToImmutable(), filePathToDocumentIds); } public TextDocumentStates UpdateStates(Func transformation, TArg arg) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 201d28946e432..a68324a694c30 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -1466,6 +1467,7 @@ internal virtual bool TryApplyChanges(Solution newSolution, IProgress(); + var result = ImmutableSortedDictionary.CreateBuilder(); // Determine if we want a major solution change, forcing regeneration of all projects. var solutionMajor = projectIds.Any(t => t.projectId is null && t.forceRegeneration); diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.resx b/src/Workspaces/Core/Portable/WorkspacesResources.resx index 7e20b01c147b4..a709923a19396 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.resx +++ b/src/Workspaces/Core/Portable/WorkspacesResources.resx @@ -495,4 +495,7 @@ Running code cleanup on fixed documents + + Applying changes to {0} + \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf index 60a243b017cfe..c0c7da7547edc 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf @@ -17,6 +17,11 @@ Došlo k chybě při čtení zadaného konfiguračního souboru: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Soubory C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf index f5a183f74084a..c8e0e89c76867 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf @@ -17,6 +17,11 @@ Beim Lesen der angegebenen Konfigurationsdatei ist ein Fehler aufgetreten: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C#-Dateien diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf index 66c1cf566b81a..688d652b9a210 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf @@ -17,6 +17,11 @@ Error al leer el archivo de configuración especificado: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Archivos de C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf index 910f067fea546..9cf1df47b76d2 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf @@ -17,6 +17,11 @@ Une erreur s'est produite lors de la lecture du fichier de configuration spécifié : {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Fichiers C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf index 131b63119784b..8132baa38dafd 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf @@ -17,6 +17,11 @@ Si è verificato un errore durante la lettura del file di configurazione specificato: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files File C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf index f6cc2a2be97c6..fb0c1c676b2e5 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf @@ -17,6 +17,11 @@ 指定した構成ファイルの読み取り中にエラーが発生しました: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# ファイル diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf index 574613a48d02b..f00171fca507c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf @@ -17,6 +17,11 @@ 지정한 구성 파일을 읽는 동안 오류가 발생했습니다({0}). + + Applying changes to {0} + Applying changes to {0} + + C# files C# 파일 diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf index 89229d1e54580..baef2a6e60e1c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf @@ -17,6 +17,11 @@ Wystąpił błąd podczas odczytywania określonego pliku konfiguracji: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Pliki C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf index 7b0d07ee6716c..5d21e0d33d94c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf @@ -17,6 +17,11 @@ Ocorreu um erro ao ler o arquivo de configuração especificado: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Arquivos C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf index 643fa036d905b..9f26b33ad0eec 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf @@ -17,6 +17,11 @@ Произошла ошибка при чтении указанного файла конфигурации: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Файлы C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf index 31523544c5c26..a80f53668d945 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf @@ -17,6 +17,11 @@ Belirtilen yapılandırma dosyası okunurken bir hata oluştu: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# dosyalarını diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf index 1a8ed118e7826..d3a06528147db 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf @@ -17,6 +17,11 @@ 读取指定的配置文件时出错: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files c# 文件 diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf index 9fd5e7497d743..bffa74e3d2aa5 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf @@ -17,6 +17,11 @@ 讀取指定的組態檔時發生錯誤: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# 檔案 diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs index b5d5d44d9e1dd..bab48686708c0 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs @@ -74,7 +74,7 @@ private static void VerifyResults(IEnumerable declarations, string[] ex } } - private Workspace CreateWorkspace(TestHost testHost = TestHost.InProcess) + private Workspace CreateWorkspace(TestHost testHost = TestHost.OutOfProcess) { var composition = FeaturesTestCompositions.Features.WithTestHostParts(testHost); var workspace = new AdhocWorkspace(composition.GetHostServices()); @@ -122,7 +122,7 @@ private Workspace CreateWorkspaceWithMultipleProjectSolution(TestHost testHost, return workspace; } - private Workspace CreateWorkspaceWithSolution(SolutionKind solutionKind, out Solution solution, TestHost testHost = TestHost.InProcess) + private Workspace CreateWorkspaceWithSolution(SolutionKind solutionKind, out Solution solution, TestHost testHost = TestHost.OutOfProcess) => solutionKind switch { SolutionKind.SingleClass => CreateWorkspaceWithSingleProjectSolution(testHost, [SingleClass], out solution), @@ -137,7 +137,7 @@ private Workspace CreateWorkspaceWithSolution(SolutionKind solutionKind, out Sol _ => throw ExceptionUtilities.UnexpectedValue(solutionKind), }; - private Workspace CreateWorkspaceWithProject(SolutionKind solutionKind, out Project project, TestHost testHost = TestHost.InProcess) + private Workspace CreateWorkspaceWithProject(SolutionKind solutionKind, out Project project, TestHost testHost = TestHost.OutOfProcess) { var workspace = CreateWorkspaceWithSolution(solutionKind, out var solution, testHost); project = solution.Projects.First(); diff --git a/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs new file mode 100644 index 0000000000000..00f06cf10e63f --- /dev/null +++ b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.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 System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public sealed class SourceGeneratorExecutionVersionMapTests +{ + [Fact] + public void TestOrderingDoesNotMatter() + { + var projectId1 = ProjectId.CreateNewId(); + var projectId2 = ProjectId.CreateNewId(); + Assert.NotEqual(projectId1, projectId2); + + var project1Kvp = new KeyValuePair(projectId1, new(MajorVersion: 1, MinorVersion: 1)); + var project2Kvp = new KeyValuePair(projectId2, new(MajorVersion: 2, MinorVersion: 2)); + + var map1 = new SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary.CreateRange([project1Kvp, project2Kvp])); + var map2 = new SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary.CreateRange([project2Kvp, project1Kvp])); + Assert.True(map1.Map.SequenceEqual(map2.Map)); + + using var memoryStream1 = SerializableBytes.CreateWritableStream(); + using var memoryStream2 = SerializableBytes.CreateWritableStream(); + { + using var writer1 = new ObjectWriter(memoryStream1, leaveOpen: true); + { + map1.WriteTo(writer1); + } + + using var writer2 = new ObjectWriter(memoryStream2, leaveOpen: true); + { + map2.WriteTo(writer2); + } + + memoryStream1.Position = 0; + memoryStream2.Position = 0; + + var array1 = memoryStream1.ToArray(); + var array2 = memoryStream2.ToArray(); + + Assert.Equal(array1.Length, array2.Length); + Assert.True(array1.Length > 0); + + Assert.True(array1.AsSpan().SequenceEqual(array2)); + + memoryStream1.Position = 0; + memoryStream2.Position = 0; + + var rehydrated1 = SourceGeneratorExecutionVersionMap.Deserialize(ObjectReader.GetReader(memoryStream1, leaveOpen: true)); + var rehydrated2 = SourceGeneratorExecutionVersionMap.Deserialize(ObjectReader.GetReader(memoryStream2, leaveOpen: true)); + + Assert.True(rehydrated1.Map.SequenceEqual(rehydrated2.Map)); + Assert.True(rehydrated1.Map.SequenceEqual(map1.Map)); + } + } +} diff --git a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs index 84c250d2915d1..6f738bcc3613f 100644 --- a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs @@ -72,7 +72,7 @@ private protected async Task AssertFormatAsync( internal void AssertFormatWithTransformation( SolutionServices services, string expected, SyntaxNode root, IEnumerable spans, SyntaxFormattingOptions options, bool treeCompare = true, ParseOptions? parseOptions = null) { - var newRootNode = Formatter.Format(root, spans, services, options, rules: null, CancellationToken.None); + var newRootNode = Formatter.Format(root, spans, services, options, rules: default, CancellationToken.None); Assert.Equal(expected, newRootNode.ToFullString()); diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 532018f46e517..83cc1ba8d7589 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -29,8 +29,6 @@ public static RemoteHostClient Create(SolutionServices services, RemoteServiceCa var inprocServices = new InProcRemoteServices(services, traceListener, testData); var instance = new InProcRemoteHostClient(services, inprocServices, callbackDispatchers); - instance.Started(); - // return instance return instance; } @@ -76,8 +74,6 @@ public override RemoteServiceConnection CreateConnection(object? callbackT public override void Dispose() { _inprocServices.Dispose(); - - base.Dispose(); } public sealed class ServiceProvider : IServiceProvider @@ -166,7 +162,7 @@ public InProcRemoteServices(SolutionServices workspaceServices, TraceListener? t { var remoteLogger = new TraceSource("InProcRemoteClient") { - Switch = { Level = SourceLevels.Verbose }, + Switch = { Level = SourceLevels.Warning }, }; if (traceListener != null) diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs index 877bff6a16c33..eda0bcadb27f4 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs @@ -22,6 +22,7 @@ using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.CodeAnalysis.UnitTests; @@ -747,5 +748,24 @@ private IList CreateSubmissions( return submissions; } + + public override bool TryApplyChanges(Solution newSolution) + { + var result = base.TryApplyChanges(newSolution); + + // Ensure that any in-memory analyzer references in this test workspace are known by the serializer service + // so that we can validate OOP scenarios involving analyzers. + foreach (var analyzer in this.CurrentSolution.AnalyzerReferences) + { + if (analyzer is AnalyzerImageReference analyzerImageReference) + { +#pragma warning disable CA1416 // Validate platform compatibility + SerializerService.TestAccessor.AddAnalyzerImageReference(analyzerImageReference); +#pragma warning restore CA1416 // Validate platform compatibility + } + } + + return result; + } } } diff --git a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj index 0908804fdba4a..fb8224323dc55 100644 --- a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj +++ b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj @@ -7,29 +7,16 @@ $(NetRoslyn);net472 - - - - - - - - - - - - - - + diff --git a/src/Workspaces/MSBuildTest/NetCoreTests.cs b/src/Workspaces/MSBuildTest/NetCoreTests.cs index d58776c25f9d9..1af40888ed5e5 100644 --- a/src/Workspaces/MSBuildTest/NetCoreTests.cs +++ b/src/Workspaces/MSBuildTest/NetCoreTests.cs @@ -6,15 +6,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.UnitTests; using Microsoft.CodeAnalysis.UnitTests.TestFiles; using Microsoft.CodeAnalysis.VisualBasic; using Roslyn.Test.Utilities; @@ -443,7 +439,7 @@ public async Task TestOpenProject_OverrideTFM() DotNetRestore(@"Library\Library.csproj"); // Override the TFM properties defined in the file - using (var workspace = CreateMSBuildWorkspace((PropertyNames.TargetFramework, ""), (PropertyNames.TargetFrameworks, "net6;net5"))) + using (var workspace = CreateMSBuildWorkspace(("TargetFramework", ""), ("TargetFrameworks", "net6;net5"))) { await workspace.OpenProjectAsync(projectFilePath); diff --git a/src/Workspaces/MSBuildTest/RpcTests.cs b/src/Workspaces/MSBuildTest/RpcTests.cs index ef05081a1ddd9..fd0e2a55f18ae 100644 --- a/src/Workspaces/MSBuildTest/RpcTests.cs +++ b/src/Workspaces/MSBuildTest/RpcTests.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.VisualStudio.Telemetry; using Nerdbank.Streams; using Xunit; diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 230b95db8ef1b..ea9cf6ad02c76 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; @@ -3116,7 +3115,6 @@ public async Task TestOpenProject_CommandLineArgsHaveNoErrors() CreateFiles(GetSimpleCSharpSolutionFiles()); using var workspace = CreateMSBuildWorkspace(); - var loader = new CSharpProjectFileLoader(); var projectFilePath = GetSolutionFileName(@"CSharpProject\CSharpProject.csproj"); @@ -3127,7 +3125,7 @@ public async Task TestOpenProject_CommandLineArgsHaveNoErrors() var projectFileInfo = (await projectFile.GetProjectFileInfosAsync(CancellationToken.None)).Single(); var commandLineParser = workspace.Services - .GetLanguageServices(loader.Language) + .GetLanguageServices(LanguageNames.CSharp) .GetRequiredService(); var projectDirectory = Path.GetDirectoryName(projectFilePath); diff --git a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs index 003a7f9ac5e8d..8060625ad45c5 100644 --- a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs @@ -105,7 +105,6 @@ await client.TryInvokeAsync( (service, cancellationToken) => service.EnableAsync(AsynchronousOperationListenerProvider.IsEnabled, listenerProvider.DiagnosticTokensEnabled, cancellationToken), cancellationToken).ConfigureAwait(false); - client.Started(); return client; } } @@ -141,8 +140,6 @@ public override void Dispose() _hubClient.Dispose(); _serviceBrokerClient.Dispose(); - - base.Dispose(); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs index 7417225aaebba..78d093bdb1e57 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Remote.Diagnostics { @@ -15,7 +16,8 @@ internal sealed class RemoteAnalyzerAssemblyLoader : AnalyzerAssemblyLoader { private readonly string _baseDirectory; - public RemoteAnalyzerAssemblyLoader(string baseDirectory) + public RemoteAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : base(externalResolvers ?? []) { _baseDirectory = baseDirectory; } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs index d1046f8960c44..52bac4993d404 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs @@ -4,11 +4,13 @@ using System; using System.Composition; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Reflection; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Remote.Diagnostics { @@ -23,13 +25,14 @@ internal sealed class RemoteAnalyzerAssemblyLoaderService : IAnalyzerAssemblyLoa [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RemoteAnalyzerAssemblyLoaderService() + public RemoteAnalyzerAssemblyLoaderService([ImportMany] IEnumerable externalResolvers) { var baseDirectory = Path.GetDirectoryName(Path.GetFullPath(typeof(RemoteAnalyzerAssemblyLoader).GetTypeInfo().Assembly.Location)); Debug.Assert(baseDirectory != null); - _loader = new(baseDirectory); - _shadowCopyLoader = new(Path.Combine(Path.GetTempPath(), "VS", "AnalyzerAssemblyLoader")); + var resolvers = externalResolvers.ToImmutableArray(); + _loader = new(baseDirectory, resolvers); + _shadowCopyLoader = new(Path.Combine(Path.GetTempPath(), "VS", "AnalyzerAssemblyLoader"), resolvers); } public IAnalyzerAssemblyLoader GetLoader(in AnalyzerAssemblyLoaderOptions options) diff --git a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs index d7cdc206882f5..c1d32bb09e5f3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -79,18 +80,22 @@ private static async Task CleanupAsync(Solution oldSolution, Solution var changes = newSolution.GetChangedDocuments(oldSolution); var final = newSolution; - foreach (var docId in changes) - { - var document = newSolution.GetRequiredDocument(docId); + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( + source: changes, + produceItems: static async (docId, callback, args, cancellationToken) => + { + var document = args.newSolution.GetRequiredDocument(docId); - var options = await document.GetCodeCleanupOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); - var cleaned = await CodeAction.CleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); + var options = await document.GetCodeCleanupOptionsAsync(args.fallbackOptions, cancellationToken).ConfigureAwait(false); + var cleaned = await CodeAction.CleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); - var cleanedRoot = await cleaned.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - final = final.WithDocumentSyntaxRoot(docId, cleanedRoot); - } + var cleanedRoot = await cleaned.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + callback((docId, cleanedRoot)); + }, + args: (newSolution, fallbackOptions), + cancellationToken).ConfigureAwait(false); - return final; + return newSolution.WithDocumentSyntaxRoots(changedDocuments); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index d73d1bc31c709..32f957d276dc4 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -307,7 +307,7 @@ static async Task WaitForHighPriorityTasksAsync(CancellationToken cancellationTo if (task.IsCompleted) { // Make sure to yield so continuations of 'task' can make progress. - await AwaitExtensions.ConfigureAwait(Task.Yield(), false); + await TaskScheduler.Default.SwitchTo(alwaysYield: true); } else { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/TypeStyle/TypeStyleHelper.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/TypeStyle/TypeStyleHelper.cs index 645e538e6d928..1cef0f1a6c039 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/TypeStyle/TypeStyleHelper.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/TypeStyle/TypeStyleHelper.cs @@ -69,7 +69,7 @@ public static bool IsTypeApparentInAssignmentExpression( } // literals, use var if options allow usage here. - if (initializerExpression.IsAnyLiteralExpression()) + if (initializerExpression is LiteralExpressionSyntax) { return stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index af8de6ed49ff5..ae491e6c78840 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -166,9 +166,6 @@ private static bool AddSimpleName(SimpleNameSyntax simpleName, List part return true; } - public static bool IsAnyLiteralExpression(this ExpressionSyntax expression) - => expression is LiteralExpressionSyntax; - public static bool IsInConstantContext([NotNullWhen(true)] this ExpressionSyntax? expression) { if (expression == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 539db54381412..0eb50260e6875 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -219,7 +219,15 @@ public static bool CanRemoveParentheses( // (null) -> null // (default) -> default; // (1) -> 1 - if (expression.IsAnyLiteralExpression()) + if (expression is LiteralExpressionSyntax) + return true; + + // (typeof(int)) -> typeof(int) + // (default(int)) -> default(int) + // (checked(1)) -> checked(1) + // (unchecked(1)) -> unchecked(1) + // (sizeof(int)) -> sizeof(int) + if (expression is TypeOfExpressionSyntax or DefaultExpressionSyntax or CheckedExpressionSyntax or SizeOfExpressionSyntax) return true; // (this) -> this diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs index e4006fad174b4..d56f6b058ed0e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs @@ -7,10 +7,8 @@ using System.Threading; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Collections; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -47,6 +45,6 @@ public override SyntaxFormattingOptions GetFormattingOptions(IOptionsReader opti protected override IFormattingResult CreateAggregatedFormattingResult(SyntaxNode node, IList results, TextSpanIntervalTree? formattingSpans = null) => new AggregatedFormattingResult(node, results, formattingSpans); - protected override AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, IEnumerable formattingRules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) + protected override AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) => new CSharpFormatEngine(node, options, formattingRules, startToken, endToken).Format(cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs index 8b392f7679a57..67fa60bca9850 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -19,7 +20,7 @@ private DefaultOperationProvider() { } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs index 8800b22006f67..792e58bfdb761 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs @@ -2,9 +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.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.LanguageService; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.LanguageService; @@ -16,7 +15,7 @@ internal class CSharpFormatEngine : AbstractFormatEngine public CSharpFormatEngine( SyntaxNode node, SyntaxFormattingOptions options, - IEnumerable formattingRules, + ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken) : base(TreeData.Create(node), @@ -30,7 +29,7 @@ public CSharpFormatEngine( internal override IHeaderFacts HeaderFacts => CSharpHeaderFacts.Instance; protected override AbstractTriviaDataFactory CreateTriviaFactory() - => new TriviaDataFactory(this.TreeData, this.Options); + => new TriviaDataFactory(this.TreeData, this.Options.LineFormatting); protected override AbstractFormattingResult CreateFormattingResult(TokenStream tokenStream) => new FormattingResult(this.TreeData, tokenStream, this.SpanToFormat); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs index 182588d383675..cec283c043333 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs @@ -43,7 +43,7 @@ private CSharpStructuredTriviaFormatEngine( internal override IHeaderFacts HeaderFacts => CSharpHeaderFacts.Instance; protected override AbstractTriviaDataFactory CreateTriviaFactory() - => new TriviaDataFactory(this.TreeData, this.Options); + => new TriviaDataFactory(this.TreeData, this.Options.LineFormatting); protected override FormattingContext CreateFormattingContext(TokenStream tokenStream, CancellationToken cancellationToken) => new(this, tokenStream); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs index 85172a3f82170..21c63a5e8f9a0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs @@ -19,9 +19,9 @@ internal partial class TriviaDataFactory /// represents a general trivia between two tokens. slightly more expensive than others since it /// needs to calculate stuff unlike other cases /// - private class ComplexTrivia : AbstractComplexTrivia + private sealed class ComplexTrivia : AbstractComplexTrivia { - public ComplexTrivia(SyntaxFormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2) + public ComplexTrivia(LineFormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2) : base(options, treeInfo, token1, token2) { } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs index 4a525360d5b33..d62c4c9a6d9a4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Formatting; internal partial class TriviaDataFactory { - private class FormattedComplexTrivia : TriviaDataWithList + private sealed class FormattedComplexTrivia : TriviaDataWithList { private readonly CSharpTriviaFormatter _formatter; private readonly IList _textChanges; @@ -27,7 +27,7 @@ public FormattedComplexTrivia( int spaces, string originalString, CancellationToken cancellationToken) - : base(context.Options, LanguageNames.CSharp) + : base(context.Options.LineFormatting) { Contract.ThrowIfNull(context); Contract.ThrowIfNull(formattingRules); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs index 5bce6fbd0fd38..1db933c501f3b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs @@ -14,12 +14,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Formatting; internal partial class TriviaDataFactory { - private class ModifiedComplexTrivia : TriviaDataWithList + private sealed class ModifiedComplexTrivia : TriviaDataWithList { private readonly ComplexTrivia _original; - public ModifiedComplexTrivia(SyntaxFormattingOptions options, ComplexTrivia original, int lineBreaks, int space) - : base(options, original.Token1.Language) + public ModifiedComplexTrivia(LineFormattingOptions options, ComplexTrivia original, int lineBreaks, int space) + : base(options) { Contract.ThrowIfNull(original); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs index a7313cb07f61c..1ec647df3cd22 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Formatting; /// internal partial class TriviaDataFactory : AbstractTriviaDataFactory { - public TriviaDataFactory(TreeData treeInfo, SyntaxFormattingOptions options) + public TriviaDataFactory(TreeData treeInfo, LineFormattingOptions options) : base(treeInfo, options) { } @@ -118,7 +118,7 @@ private static bool ContainsOnlyWhitespace(Analyzer.AnalysisResult result) { // calculate actual space size from tab var spaces = CalculateSpaces(token1, token2); - return new ModifiedWhitespace(this.Options, result.LineBreaks, indentation: spaces, elastic: result.TreatAsElastic, language: LanguageNames.CSharp); + return new ModifiedWhitespace(this.Options, result.LineBreaks, indentation: spaces, elastic: result.TreatAsElastic); } // check whether we can cache trivia info for current indentation diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs index d2c5f95c7cc6b..d8259513c82c9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -116,13 +117,13 @@ protected static void SetAlignmentBlockOperation( list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(baseToken, startToken, endToken, indentationDelta: 0, option: option)); } - protected static void AddSuppressWrappingIfOnSingleLineOperation(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) + protected static void AddSuppressWrappingIfOnSingleLineOperation(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) => AddSuppressOperation(list, startToken, endToken, SuppressOption.NoWrappingIfOnSingleLine | extraOption); - protected static void AddSuppressAllOperationIfOnMultipleLine(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) + protected static void AddSuppressAllOperationIfOnMultipleLine(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) => AddSuppressOperation(list, startToken, endToken, SuppressOption.NoSpacingIfOnMultipleLine | SuppressOption.NoWrapping | extraOption); - protected static void AddSuppressOperation(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption option) + protected static void AddSuppressOperation(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption option) { if (startToken.Kind() == SyntaxKind.None || endToken.Kind() == SyntaxKind.None) { @@ -158,7 +159,7 @@ protected static AdjustNewLinesOperation CreateAdjustNewLinesOperation(int line, protected static AdjustSpacesOperation CreateAdjustSpacesOperation(int space, AdjustSpacesOption option) => FormattingOperations.CreateAdjustSpacesOperation(space, option); - protected static void AddBraceSuppressOperations(List list, SyntaxNode node) + protected static void AddBraceSuppressOperations(ArrayBuilder list, SyntaxNode node) { var bracePair = node.GetBracePair(); if (!bracePair.IsValidBracketOrBracePair()) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs index f3b79019b4238..977c5ebd4978a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -21,7 +22,7 @@ internal class ElasticTriviaFormattingRule : BaseFormattingRule { internal const string Name = "CSharp Elastic trivia Formatting Rule"; - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -37,7 +38,7 @@ public override void AddSuppressOperations(List list, SyntaxN AddCollectionExpressionSuppressOperations(list, node); } - private static void AddPropertyDeclarationSuppressOperations(List list, SyntaxNode node) + private static void AddPropertyDeclarationSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is BasePropertyDeclarationSyntax basePropertyDeclaration && basePropertyDeclaration.AccessorList != null && basePropertyDeclaration.AccessorList.Accessors.All(a => a.Body == null) && @@ -49,7 +50,7 @@ private static void AddPropertyDeclarationSuppressOperations(List list, SyntaxNode node) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode node) { var initializer = GetInitializerNode(node); var lastTokenOfType = GetLastTokenOfType(node); @@ -66,7 +67,7 @@ private static void AddInitializerSuppressOperations(List lis } } - private static void AddCollectionExpressionSuppressOperations(List list, SyntaxNode node) + private static void AddCollectionExpressionSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is CollectionExpressionSyntax { OpenBracketToken.IsMissing: false, CloseBracketToken.IsMissing: false } collectionExpression) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs index 32bdf04b114db..e9628ccd59744 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -38,7 +39,7 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio return new QueryExpressionFormattingRule(newOptions); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs index 68df98b39dfce..ccf10986adf30 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -557,7 +558,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return nextOperation.Invoke(in previousToken, in currentToken); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -570,7 +571,7 @@ private static bool IsEmptyForStatement(ForStatementSyntax forStatement) && forStatement.Condition == null && forStatement.Incrementors.Count == 0; - private void SuppressVariableDeclaration(List list, SyntaxNode node) + private void SuppressVariableDeclaration(ArrayBuilder list, SyntaxNode node) { if (node.Kind() is SyntaxKind.FieldDeclaration diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs index d649ba761a44c..dd4aecbdbe93c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -17,7 +18,7 @@ internal class SuppressFormattingRule : BaseFormattingRule { internal const string Name = "CSharp Suppress Formatting Rule"; - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -32,7 +33,7 @@ public override void AddSuppressOperations(List list, SyntaxN AddSpecificNodesSuppressOperations(list, node); } - private static void AddSpecificNodesSuppressOperations(List list, SyntaxNode node) + private static void AddSpecificNodesSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is IfStatementSyntax ifStatementNode) { @@ -259,7 +260,7 @@ private static void AddSpecificNodesSuppressOperations(List l } } - private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddStatementExceptBlockSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block) { @@ -272,7 +273,7 @@ private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddFormatSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (!node.ContainsDirectives) { @@ -293,7 +294,7 @@ private static void AddFormatSuppressOperations(List list, Sy return; // Local functions - static void ProcessTriviaList(List list, SyntaxTriviaList triviaList) + static void ProcessTriviaList(ArrayBuilder list, SyntaxTriviaList triviaList) { foreach (var trivia in triviaList) { @@ -301,7 +302,7 @@ static void ProcessTriviaList(List list, SyntaxTriviaList tri } } - static void ProcessTrivia(List list, SyntaxTrivia trivia) + static void ProcessTrivia(ArrayBuilder list, SyntaxTrivia trivia) { if (!(trivia.HasStructure)) { @@ -311,7 +312,7 @@ static void ProcessTrivia(List list, SyntaxTrivia trivia) ProcessStructuredTrivia(list, trivia.GetStructure()!); } - static void ProcessStructuredTrivia(List list, SyntaxNode structure) + static void ProcessStructuredTrivia(ArrayBuilder list, SyntaxNode structure) { if (structure is not PragmaWarningDirectiveTriviaSyntax pragmaWarningDirectiveTrivia) { @@ -365,7 +366,7 @@ private static bool IsFormatDirective(DirectiveTriviaSyntax? trivia, SyntaxKind return false; } - private static void AddInitializerSuppressOperations(List list, SyntaxNode node) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode node) { // array or collection initializer case if (node.IsInitializerForArrayOrCollectionCreationExpression()) @@ -395,7 +396,7 @@ private static void AddInitializerSuppressOperations(List lis } } - private static void AddInitializerSuppressOperations(List list, SyntaxNode parent, IEnumerable items) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode parent, IEnumerable items) { // make creation node itself to not break into multiple line, if it is on same line AddSuppressWrappingIfOnSingleLineOperation(list, parent.GetFirstToken(includeZeroWidth: true), parent.GetLastToken(includeZeroWidth: true)); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs index 7332ca7cbdf80..1e41f578424e3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -42,7 +43,7 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio return new WrappingFormattingRule(newOptions); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -88,7 +89,7 @@ private static (SyntaxToken firstToken, SyntaxToken lastToken) GetSpecificNodeSu }; } - private static void AddSpecificNodesSuppressOperations(List list, SyntaxNode node) + private static void AddSpecificNodesSuppressOperations(ArrayBuilder list, SyntaxNode node) { var (firstToken, lastToken) = GetSpecificNodeSuppressionTokenRange(node); if (!firstToken.IsKind(SyntaxKind.None) || !lastToken.IsKind(SyntaxKind.None)) @@ -97,7 +98,7 @@ private static void AddSpecificNodesSuppressOperations(List l } } - private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddStatementExceptBlockSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block) { @@ -110,7 +111,7 @@ private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void RemoveSuppressOperationForStatementMethodDeclaration(ArrayBuilder list, SyntaxNode node) { if (!(node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block)) { @@ -133,7 +134,7 @@ private static void RemoveSuppressOperationForStatementMethodDeclaration(List list, SyntaxNode node) + private static void RemoveSuppressOperationForBlock(ArrayBuilder list, SyntaxNode node) { var bracePair = GetBracePair(node); if (!bracePair.IsValidBracketOrBracePair()) @@ -175,7 +176,7 @@ private static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(Synt } private static void RemoveSuppressOperation( - List list, + ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs index 2e538e86dd901..afd0a3984bc56 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -103,7 +104,7 @@ public IList FormatToken(SyntaxToken token, CancellationToken cancel } } - var smartTokenformattingRules = new SmartTokenFormattingRule().Concat(_formattingRules); + ImmutableArray smartTokenFormattingRules = [new SmartTokenFormattingRule(), .. _formattingRules]; var adjustedStartPosition = previousToken.SpanStart; if (token.IsKind(SyntaxKind.OpenBraceToken) && _options.IndentStyle != FormattingOptions2.IndentStyle.Smart) @@ -117,7 +118,7 @@ public IList FormatToken(SyntaxToken token, CancellationToken cancel var formatter = CSharpSyntaxFormatting.Instance; var result = formatter.GetFormattingResult( - _root, [TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition)], _options.FormattingOptions, smartTokenformattingRules, cancellationToken); + _root, [TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition)], _options.FormattingOptions, smartTokenFormattingRules, cancellationToken); return result.GetTextChanges(cancellationToken); } @@ -146,7 +147,7 @@ private class NoLineChangeFormattingRule : AbstractFormattingRule private class SmartTokenFormattingRule : NoLineChangeFormattingRule { - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { // don't suppress anything } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 0e0c3b9ca927a..f90939d8824f0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -22,17 +23,18 @@ internal partial class IntervalTree : IEnumerable { public static readonly IntervalTree Empty = new(); - protected Node? root; + private static readonly ObjectPool> s_stackPool + = SharedPools.Default>(); + + /// + /// Keep around a fair number of these as we often use them in parallel algorithms. + /// + private static readonly ObjectPool> s_nodePool = new(() => new(), 128); private delegate bool TestInterval(T value, int start, int length, in TIntrospector introspector) where TIntrospector : struct, IIntervalIntrospector; - private static readonly ObjectPool> s_stackPool - = SharedPools.Default>(); - - public IntervalTree() - { - } + protected Node? root; public static IntervalTree Create(in TIntrospector introspector, IEnumerable values) where TIntrospector : struct, IIntervalIntrospector @@ -139,9 +141,33 @@ public bool HasIntervalThatContains(int start, int length, in TIn private bool Any(int start, int length, TestInterval testInterval, in TIntrospector introspector) where TIntrospector : struct, IIntervalIntrospector { - using var result = TemporaryArray.Empty; - var matches = FillWithIntervalsThatMatch(start, length, testInterval, ref result.AsRef(), in introspector, stopAfterFirst: true); - return matches > 0; + // Inlined version of FillWithIntervalsThatMatch, optimized to do less work and stop once it finds a match. + if (root is null) + return false; + + using var pooledObject = s_nodePool.GetPooledObject(); + var candidates = pooledObject.Object; + + var end = start + length; + + candidates.Push(root); + + while (candidates.TryPop(out var currentNode)) + { + // Check the nodes as we go down. That way we can stop immediately when we find something that matches, + // instead of having to do an entire in-order walk, which might end up hitting a lot of nodes we don't care + // about and placing a lot into the stack. + if (testInterval(currentNode.Value, start, length, in introspector)) + return true; + + if (ShouldExamineRight(start, end, currentNode, in introspector, out var right)) + candidates.Push(right); + + if (ShouldExamineLeft(start, currentNode, in introspector, out var left)) + candidates.Push(left); + } + + return false; } private ImmutableArray GetIntervalsThatMatch( @@ -161,28 +187,11 @@ private int FillWithIntervalsThatMatch( where TIntrospector : struct, IIntervalIntrospector { if (root == null) - { return 0; - } using var pooledObject = s_stackPool.GetPooledObject(); var candidates = pooledObject.Object; - var matches = FillWithIntervalsThatMatch( - start, length, testInterval, - ref builder, in introspector, - stopAfterFirst, candidates); - - return matches; - } - - /// The number of matching intervals found by the method. - private int FillWithIntervalsThatMatch( - int start, int length, TestInterval testInterval, - ref TemporaryArray builder, in TIntrospector introspector, - bool stopAfterFirst, Stack<(Node? node, bool firstTime)> candidates) - where TIntrospector : struct, IIntervalIntrospector - { var matches = 0; var end = start + length; @@ -191,11 +200,8 @@ private int FillWithIntervalsThatMatch( while (candidates.TryPop(out var currentTuple)) { var currentNode = currentTuple.node; - RoslynDebug.Assert(currentNode != null); - - var firstTime = currentTuple.firstTime; - if (!firstTime) + if (!currentTuple.firstTime) { // We're seeing this node for the second time (as we walk back up the left // side of it). Now see if it matches our test, and if so return it out. @@ -205,45 +211,62 @@ private int FillWithIntervalsThatMatch( builder.Add(currentNode.Value); if (stopAfterFirst) - { return 1; - } } } else { - // First time we're seeing this node. In order to see the node 'in-order', - // we push the right side, then the node again, then the left side. This - // time we mark the current node with 'false' to indicate that it's the - // second time we're seeing it the next time it comes around. - - // right children's starts will never be to the left of the parent's start - // so we should consider right subtree only if root's start overlaps with - // interval's End, - if (introspector.GetStart(currentNode.Value) <= end) - { - var right = currentNode.Right; - if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start) - { - candidates.Push((right, firstTime: true)); - } - } + // First time we're seeing this node. In order to see the node 'in-order', we push the right side, then + // the node again, then the left side. This time we mark the current node with 'false' to indicate that + // it's the second time we're seeing it the next time it comes around. + + if (ShouldExamineRight(start, end, currentNode, in introspector, out var right)) + candidates.Push((right, firstTime: true)); candidates.Push((currentNode, firstTime: false)); - // only if left's maxVal overlaps with interval's start, we should consider - // left subtree - var left = currentNode.Left; - if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) - { + if (ShouldExamineLeft(start, currentNode, in introspector, out var left)) candidates.Push((left, firstTime: true)); - } } } return matches; } + private static bool ShouldExamineRight( + int start, int end, + Node currentNode, + in TIntrospector introspector, + [NotNullWhen(true)] out Node? right) where TIntrospector : struct, IIntervalIntrospector + { + // right children's starts will never be to the left of the parent's start so we should consider right + // subtree only if root's start overlaps with interval's End, + if (introspector.GetStart(currentNode.Value) <= end) + { + right = currentNode.Right; + if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start) + return true; + } + + right = null; + return false; + } + + private static bool ShouldExamineLeft( + int start, + Node currentNode, + in TIntrospector introspector, + [NotNullWhen(true)] out Node? left) where TIntrospector : struct, IIntervalIntrospector + { + // only if left's maxVal overlaps with interval's start, we should consider + // left subtree + left = currentNode.Left; + if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) + return true; + + return false; + } + public bool IsEmpty() => this.root == null; protected static Node Insert(Node? root, Node newNode, in TIntrospector introspector) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 535a7e3fdb495..4a05b3ffbfe13 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; @@ -58,6 +59,25 @@ public static void RemoveOrTransformAll(this List list, Func(this ArrayBuilder list, Func transform, TArg arg) + where T : struct + { + RoslynDebug.AssertNotNull(list); + RoslynDebug.AssertNotNull(transform); + + var targetIndex = 0; + for (var sourceIndex = 0; sourceIndex < list.Count; sourceIndex++) + { + var newValue = transform(list[sourceIndex], arg); + if (newValue is null) + continue; + + list[targetIndex++] = newValue.Value; + } + + list.RemoveRange(targetIndex, list.Count - targetIndex); + } + /// /// Attempts to remove the first item selected by . /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs index 782139a28ab85..67fe1c29206da 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxNodeExtensions.cs @@ -178,12 +178,18 @@ public static SyntaxNode GetCommonRoot(this SyntaxNode node1, SyntaxNode node2) { Contract.ThrowIfTrue(node1.RawKind == 0 || node2.RawKind == 0); - // find common starting node from two nodes. - // as long as two nodes belong to same tree, there must be at least one common root (Ex, compilation unit) - var ancestors = node1.GetAncestorsOrThis(); - var set = new HashSet(node2.GetAncestorsOrThis()); + // find common starting node from two nodes. as long as two nodes belong to same tree, there must be at least + // one common root (Ex, compilation unit) + using var _ = PooledHashSet.GetInstance(out var set); + set.AddRange(node2.GetAncestorsOrThis()); - return ancestors.First(set.Contains); + foreach (var ancestor in node1.AncestorsAndSelf()) + { + if (set.Contains(ancestor)) + return ancestor; + } + + throw ExceptionUtilities.Unreachable(); } public static int Width(this SyntaxNode node) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs index 662e591c5012e..22865e0751e08 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs @@ -7,14 +7,12 @@ using System.Collections.Immutable; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; @@ -22,10 +20,6 @@ internal abstract class AbstractSyntaxFormatting : ISyntaxFormatting { private static readonly Func s_notEmpty = s => !s.IsEmpty; - protected AbstractSyntaxFormatting() - { - } - public abstract SyntaxFormattingOptions DefaultOptions { get; } public abstract SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); @@ -33,9 +27,9 @@ protected AbstractSyntaxFormatting() protected abstract IFormattingResult CreateAggregatedFormattingResult(SyntaxNode node, IList results, TextSpanIntervalTree? formattingSpans = null); - protected abstract AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, IEnumerable rules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken); + protected abstract AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, ImmutableArray rules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken); - public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { IReadOnlyList spansToFormat; @@ -53,7 +47,8 @@ public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? results = null; foreach (var (startToken, endToken) in node.ConvertToTokenPairs(spansToFormat)) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs index d47e5050dadfa..d0a46391100f9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs @@ -219,7 +219,7 @@ private List GetParentIndentBlockOperations(SyntaxToken to allNodes.Do(n => _formattingRules.AddIndentBlockOperations(list, n)); // sort them in right order - list.RemoveAll(CommonFormattingHelpers.IsNull); + list.RemoveAll(static o => o is null); list.Sort(CommonFormattingHelpers.IndentBlockOperationComparer); return list; @@ -296,7 +296,7 @@ private SyntaxToken GetAlignmentBaseTokenFor(SyntaxToken token) } // well, found no appropriate one - list.RemoveAll(CommonFormattingHelpers.IsNull); + list.RemoveAll(static o => o is null); if (list.Count == 0) { return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs index b49d180422640..641931ca4e693 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -39,7 +41,7 @@ public InitialContextFinder( _rootNode = rootNode; } - public (List indentOperations, List? suppressOperations) Do(SyntaxToken startToken, SyntaxToken endToken) + public (List indentOperations, ImmutableArray suppressOperations) Do(SyntaxToken startToken, SyntaxToken endToken) { // we are formatting part of document, try to find initial context that formatting will be based on such as // initial indentation and etc. @@ -53,7 +55,6 @@ public InitialContextFinder( if (initialSuppressOperations != null) { Debug.Assert( - initialSuppressOperations.IsEmpty() || initialSuppressOperations.All( o => o.TextSpan.Contains(startToken.SpanStart) || o.TextSpan.Contains(endToken.SpanStart))); @@ -121,71 +122,60 @@ private List GetInitialIndentBlockOperations(SyntaxToken s return operations; } - private List? GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken) + private ImmutableArray GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken) { - var noWrapList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoWrapping); - var noSpaceList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoSpacing); + using var _ = ArrayBuilder.GetInstance(out var result); - var list = noWrapList.Combine(noSpaceList); - if (list == null) - { - return null; - } + this.AddInitialSuppressOperations(startToken, endToken, SuppressOption.NoWrapping, result); + this.AddInitialSuppressOperations(startToken, endToken, SuppressOption.NoSpacing, result); - list.Sort(CommonFormattingHelpers.SuppressOperationComparer); - return list; + result.Sort(CommonFormattingHelpers.SuppressOperationComparer); + return result.ToImmutable(); } - private List? GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken, SuppressOption mask) + private void AddInitialSuppressOperations( + SyntaxToken startToken, SyntaxToken endToken, SuppressOption mask, ArrayBuilder result) { - var startList = this.GetInitialSuppressOperations(startToken, mask); - var endList = this.GetInitialSuppressOperations(endToken, mask); - - return startList.Combine(endList); + this.AddInitialSuppressOperations(startToken, mask, result); + this.AddInitialSuppressOperations(endToken, mask, result); } - private List? GetInitialSuppressOperations(SyntaxToken token, SuppressOption mask) + private void AddInitialSuppressOperations(SyntaxToken token, SuppressOption mask, ArrayBuilder result) { var startNode = token.Parent; var startPosition = token.SpanStart; // starting from given token, move up to root until the first meaningful // operation has found - var list = new List(); + using var _ = ArrayBuilder.GetInstance(out var buffer); var currentIndentationNode = startNode; - Predicate predicate = Predicate; while (currentIndentationNode != null) { - _formattingRules.AddSuppressOperations(list, currentIndentationNode); + _formattingRules.AddSuppressOperations(buffer, currentIndentationNode); - list.RemoveAll(predicate); - if (list.Count > 0) + buffer.RemoveAll(Predicate, (startPosition, _tokenStream, mask)); + if (buffer.Count > 0) { - return list; + result.AddRange(buffer); + return; } currentIndentationNode = currentIndentationNode.Parent; } - return null; + return; - bool Predicate(SuppressOperation operation) + static bool Predicate(SuppressOperation operation, (int startPosition, TokenStream tokenStream, SuppressOption mask) tuple) { - if (!operation.TextSpan.Contains(startPosition)) - { + if (!operation.TextSpan.Contains(tuple.startPosition)) return true; - } - if (operation.ContainsElasticTrivia(_tokenStream) && !operation.Option.IsOn(SuppressOption.IgnoreElasticWrapping)) - { + if (operation.ContainsElasticTrivia(tuple.tokenStream) && !operation.Option.IsOn(SuppressOption.IgnoreElasticWrapping)) return true; - } - if (!operation.Option.IsMaskOn(mask)) - { + if (!operation.Option.IsMaskOn(tuple.mask)) return true; - } return false; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index b408318b2916e..965a3a2b38a6f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -115,7 +115,8 @@ public void Initialize( _initialIndentBlockOperations = indentationOperations; } - suppressOperations?.Do(this.AddInitialSuppressOperation); + foreach (var suppressOperation in suppressOperations) + this.AddInitialSuppressOperation(suppressOperation); } public void AddIndentBlockOperations( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 60cd1f2e7f4fa..83886f8fdf3b6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Collections; @@ -45,21 +46,44 @@ internal abstract partial class AbstractFormatEngine internal readonly SyntaxFormattingOptions Options; internal readonly TreeData TreeData; + /// + /// It is very common to be formatting lots of documents at teh same time, with the same set of formatting rules and + /// options. To help with that, cache the last set of ChainedFormattingRules that was produced, as it is not a cheap + /// type to create. + /// + /// + /// Stored as a instead of a so we don't have + /// to worry about torn write concerns. + /// + private static Tuple, SyntaxFormattingOptions, ChainedFormattingRules>? s_lastRulesAndOptions; + public AbstractFormatEngine( TreeData treeData, SyntaxFormattingOptions options, - IEnumerable formattingRules, + ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken) : this( treeData, options, - new ChainedFormattingRules(formattingRules, options), + GetChainedFormattingRules(formattingRules, options), startToken, endToken) { } + private static ChainedFormattingRules GetChainedFormattingRules(ImmutableArray formattingRules, SyntaxFormattingOptions options) + { + var lastRulesAndOptions = s_lastRulesAndOptions; + if (formattingRules != lastRulesAndOptions?.Item1 || options != s_lastRulesAndOptions?.Item2) + { + lastRulesAndOptions = Tuple.Create(formattingRules, options, new ChainedFormattingRules(formattingRules, options)); + s_lastRulesAndOptions = lastRulesAndOptions; + } + + return lastRulesAndOptions.Item3; + } + internal AbstractFormatEngine( TreeData treeData, SyntaxFormattingOptions options, @@ -133,10 +157,10 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella var nodeOperations = new NodeOperations(); - var indentBlockOperation = new List(); - var suppressOperation = new List(); - var alignmentOperation = new List(); - var anchorIndentationOperations = new List(); + var indentBlockOperationScratch = new List(); + var alignmentOperationScratch = new List(); + var anchorIndentationOperationsScratch = new List(); + using var _ = ArrayBuilder.GetInstance(out var suppressOperationScratch); // Cache delegates out here to avoid allocation overhead. @@ -150,14 +174,14 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella { cancellationToken.ThrowIfCancellationRequested(); - AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperation, node, addIndentBlockOperations); - AddOperations(nodeOperations.SuppressOperation, suppressOperation, node, addSuppressOperation); - AddOperations(nodeOperations.AlignmentOperation, alignmentOperation, node, addAlignTokensOperations); - AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperations, node, addAnchorIndentationOperations); + AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperationScratch, node, addIndentBlockOperations); + AddOperations(nodeOperations.SuppressOperation, suppressOperationScratch, node, addSuppressOperation); + AddOperations(nodeOperations.AlignmentOperation, alignmentOperationScratch, node, addAlignTokensOperations); + AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperationsScratch, node, addAnchorIndentationOperations); } // make sure we order align operation from left to right - alignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); + nodeOperations.AlignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); return nodeOperations; } @@ -176,6 +200,20 @@ private static void AddOperations(SegmentedList operations, List scratc scratch.Clear(); } + private static void AddOperations(SegmentedList operations, ArrayBuilder scratch, SyntaxNode node, Action, SyntaxNode> addOperations) + { + Debug.Assert(scratch.Count == 0); + + addOperations(scratch, node); + foreach (var operation in scratch) + { + if (operation is not null) + operations.Add(operation); + } + + scratch.Clear(); + } + private void AddTokenOperations( TokenStream tokenStream, SegmentedList list, 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 8e14ebe0b9565..c081c5813c4b4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -21,8 +20,8 @@ protected abstract class AbstractComplexTrivia : TriviaDataWithList private readonly bool _treatAsElastic; - public AbstractComplexTrivia(SyntaxFormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2) - : base(options, token1.Language) + public AbstractComplexTrivia(LineFormattingOptions options, TreeData treeInfo, SyntaxToken token1, SyntaxToken token2) + : base(options) { Contract.ThrowIfNull(treeInfo); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs index 82ae7747c65b3..2e1c7f1a60ce4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs @@ -13,12 +13,12 @@ namespace Microsoft.CodeAnalysis.Formatting; internal abstract partial class AbstractTriviaDataFactory { - protected class FormattedWhitespace : TriviaData + protected sealed class FormattedWhitespace : TriviaData { private readonly string _newString; - public FormattedWhitespace(SyntaxFormattingOptions options, int lineBreaks, int indentation, string language) - : base(options, language) + public FormattedWhitespace(LineFormattingOptions options, int lineBreaks, int indentation) + : base(options) { this.LineBreaks = Math.Max(0, lineBreaks); this.Spaces = Math.Max(0, indentation); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs index bca9cdecd1006..474e09b6a354d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs @@ -11,18 +11,18 @@ namespace Microsoft.CodeAnalysis.Formatting; internal abstract partial class AbstractTriviaDataFactory { - protected class ModifiedWhitespace : Whitespace + protected sealed class ModifiedWhitespace : Whitespace { private readonly Whitespace? _original; - public ModifiedWhitespace(SyntaxFormattingOptions options, int lineBreaks, int indentation, bool elastic, string language) - : base(options, lineBreaks, indentation, elastic, language) + public ModifiedWhitespace(LineFormattingOptions options, int lineBreaks, int indentation, bool elastic) + : base(options, lineBreaks, indentation, elastic) { _original = null; } - public ModifiedWhitespace(SyntaxFormattingOptions options, Whitespace original, int lineBreaks, int indentation, bool elastic, string language) - : base(options, lineBreaks, indentation, elastic, language) + public ModifiedWhitespace(LineFormattingOptions options, Whitespace original, int lineBreaks, int indentation, bool elastic) + : base(options, lineBreaks, indentation, elastic) { Contract.ThrowIfNull(original); _original = original; @@ -83,7 +83,7 @@ public override void Format( CancellationToken cancellationToken, int tokenPairIndex = TokenPairIndexNotNeeded) { - formattingResultApplier(tokenPairIndex, context.TokenStream, new FormattedWhitespace(this.Options, this.LineBreaks, this.Spaces, this.Language)); + formattingResultApplier(tokenPairIndex, context.TokenStream, new FormattedWhitespace(this.Options, this.LineBreaks, this.Spaces)); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs index 116dda5456313..bda9b5b3785b4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs @@ -21,14 +21,14 @@ protected class Whitespace : TriviaData { private readonly bool _elastic; - public Whitespace(SyntaxFormattingOptions options, int space, bool elastic, string language) - : this(options, lineBreaks: 0, indentation: space, elastic: elastic, language: language) + public Whitespace(LineFormattingOptions options, int space, bool elastic) + : this(options, lineBreaks: 0, indentation: space, elastic: elastic) { Contract.ThrowIfFalse(space >= 0); } - public Whitespace(SyntaxFormattingOptions options, int lineBreaks, int indentation, bool elastic, string language) - : base(options, language) + public Whitespace(LineFormattingOptions options, int lineBreaks, int indentation, bool elastic) + : base(options) { _elastic = elastic; @@ -47,11 +47,9 @@ public Whitespace(SyntaxFormattingOptions options, int lineBreaks, int indentati public override TriviaData WithSpace(int space, FormattingContext context, ChainedFormattingRules formattingRules) { if (this.LineBreaks == 0 && this.Spaces == space) - { return this; - } - return new ModifiedWhitespace(this.Options, this, /*lineBreak*/0, space, elastic: false, language: this.Language); + return new ModifiedWhitespace(this.Options, this, /*lineBreak*/0, space, elastic: false); } public override TriviaData WithLine(int line, int indentation, FormattingContext context, ChainedFormattingRules formattingRules, CancellationToken cancellationToken) @@ -63,7 +61,7 @@ public override TriviaData WithLine(int line, int indentation, FormattingContext return this; } - return new ModifiedWhitespace(this.Options, this, line, indentation, elastic: false, language: this.Language); + return new ModifiedWhitespace(this.Options, this, line, indentation, elastic: false); } public override TriviaData WithIndentation( @@ -74,7 +72,7 @@ public override TriviaData WithIndentation( return this; } - return new ModifiedWhitespace(this.Options, this, this.LineBreaks, indentation, elastic: false, language: this.Language); + return new ModifiedWhitespace(this.Options, this, this.LineBreaks, indentation, elastic: false); } public override void Format( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs index 19b2882c81115..71a0fce380856 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; +using System.Security.Principal; using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Roslyn.Utilities; @@ -14,23 +17,73 @@ internal abstract partial class AbstractTriviaDataFactory private const int LineBreakCacheSize = 5; private const int IndentationLevelCacheSize = 20; + private static readonly Dictionary s_optionsToWhitespace = new(); + private static Tuple? s_lastOptionAndWhitespace; + protected readonly TreeData TreeInfo; - protected readonly SyntaxFormattingOptions Options; + protected readonly LineFormattingOptions Options; private readonly Whitespace[] _spaces; - private readonly Whitespace?[,] _whitespaces = new Whitespace[LineBreakCacheSize, IndentationLevelCacheSize]; + private readonly Whitespace[,] _whitespaces; - protected AbstractTriviaDataFactory(TreeData treeInfo, SyntaxFormattingOptions options) + protected AbstractTriviaDataFactory(TreeData treeInfo, LineFormattingOptions options) { Contract.ThrowIfNull(treeInfo); TreeInfo = treeInfo; Options = options; - _spaces = new Whitespace[SpaceCacheSize]; - for (var i = 0; i < SpaceCacheSize; i++) + (_spaces, _whitespaces) = GetSpacesAndWhitespaces(options); + } + + private static (Whitespace[] spaces, Whitespace[,] whitespaces) GetSpacesAndWhitespaces(LineFormattingOptions options) + { + // Fast path where we'er asking for the same options as last time + var lastOptionAndWhitespace = s_lastOptionAndWhitespace; + if (lastOptionAndWhitespace?.Item1 == options) + return lastOptionAndWhitespace.Item2; + + // Otherwise, get from the dictionary, computing if necessary. + var (spaces, whitespaces) = ComputeAndCacheSpacesAndWhitespaces(options); + + // Cache this result for the next time. + s_lastOptionAndWhitespace = Tuple.Create(options, (spaces, whitespaces)); + return (spaces, whitespaces); + + static (Whitespace[] spaces, Whitespace[,] whitespaces) ComputeAndCacheSpacesAndWhitespaces(LineFormattingOptions options) { - _spaces[i] = new Whitespace(Options, space: i, elastic: false, language: treeInfo.Root.Language); + // First check if it's already in the cache. + lock (s_optionsToWhitespace) + { + if (s_optionsToWhitespace.TryGetValue(options, out var result)) + return result; + } + + // If not, compute it. + var spaces = new Whitespace[SpaceCacheSize]; + for (var i = 0; i < SpaceCacheSize; i++) + spaces[i] = new Whitespace(options, space: i, elastic: false); + + var whitespaces = new Whitespace[LineBreakCacheSize, IndentationLevelCacheSize]; + for (var lineIndex = 0; lineIndex < LineBreakCacheSize; lineIndex++) + { + for (var indentationLevel = 0; indentationLevel < IndentationLevelCacheSize; indentationLevel++) + { + var indentation = indentationLevel * options.IndentationSize; + whitespaces[lineIndex, indentationLevel] = new Whitespace( + options, lineBreaks: lineIndex + 1, indentation: indentation, elastic: false); + } + } + + // Attempt to store in cache. But defer to any other thread that may have already stored it. + lock (s_optionsToWhitespace) + { + if (s_optionsToWhitespace.TryGetValue(options, out var result)) + return result; + + s_optionsToWhitespace[options] = (spaces, whitespaces); + return (spaces, whitespaces); + } } } @@ -41,7 +94,7 @@ protected TriviaData GetSpaceTriviaData(int space, bool elastic = false) // if result has elastic trivia in them, never use cache if (elastic) { - return new Whitespace(this.Options, space, elastic: true, language: this.TreeInfo.Root.Language); + return new Whitespace(this.Options, space, elastic: true); } if (space < SpaceCacheSize) @@ -50,7 +103,7 @@ protected TriviaData GetSpaceTriviaData(int space, bool elastic = false) } // create a new space - return new Whitespace(this.Options, space, elastic: false, language: this.TreeInfo.Root.Language); + return new Whitespace(this.Options, space, elastic: false); } protected TriviaData GetWhitespaceTriviaData(int lineBreaks, int indentation, bool useTriviaAsItIs, bool elastic) @@ -75,29 +128,13 @@ protected TriviaData GetWhitespaceTriviaData(int lineBreaks, int indentation, bo if (indentationLevel < IndentationLevelCacheSize) { var lineIndex = lineBreaks - 1; - EnsureWhitespaceTriviaInfo(lineIndex, indentationLevel); return _whitespaces[lineIndex, indentationLevel]!; } } - return - useTriviaAsItIs - ? new Whitespace(this.Options, lineBreaks, indentation, elastic, language: this.TreeInfo.Root.Language) - : new ModifiedWhitespace(this.Options, lineBreaks, indentation, elastic, language: this.TreeInfo.Root.Language); - } - - private void EnsureWhitespaceTriviaInfo(int lineIndex, int indentationLevel) - { - Contract.ThrowIfFalse(lineIndex is >= 0 and < LineBreakCacheSize); - Contract.ThrowIfFalse(indentationLevel >= 0 && indentationLevel < _whitespaces.Length / _whitespaces.Rank); - - // set up caches - if (_whitespaces[lineIndex, indentationLevel] == null) - { - var indentation = indentationLevel * Options.IndentationSize; - var triviaInfo = new Whitespace(Options, lineBreaks: lineIndex + 1, indentation: indentation, elastic: false, language: this.TreeInfo.Root.Language); - Interlocked.CompareExchange(ref _whitespaces[lineIndex, indentationLevel], triviaInfo, null); - } + return useTriviaAsItIs + ? new Whitespace(this.Options, lineBreaks, indentation, elastic) + : new ModifiedWhitespace(this.Options, lineBreaks, indentation, elastic); } public abstract TriviaData CreateLeadingTrivia(SyntaxToken token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs index b0dbfa8d1204c..68f2d1c86db2d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs @@ -10,6 +10,7 @@ using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; @@ -32,7 +33,7 @@ public ChainedFormattingRules(IEnumerable formattingRule { Contract.ThrowIfNull(formattingRules); - _formattingRules = formattingRules.Select(rule => rule.WithOptions(options)).ToImmutableArray(); + _formattingRules = formattingRules.SelectAsArray(rule => rule.WithOptions(options)); _options = options; _addSuppressOperationsRules = FilterToRulesImplementingMethod(_formattingRules, nameof(AbstractFormattingRule.AddSuppressOperations)); @@ -43,7 +44,7 @@ public ChainedFormattingRules(IEnumerable formattingRule _getAdjustSpacesOperationRules = FilterToRulesImplementingMethod(_formattingRules, nameof(AbstractFormattingRule.GetAdjustSpacesOperation)); } - public void AddSuppressOperations(List list, SyntaxNode currentNode) + public void AddSuppressOperations(ArrayBuilder list, SyntaxNode currentNode) { var action = new NextSuppressOperationAction(_addSuppressOperationsRules, index: 0, currentNode, list); action.Invoke(); @@ -81,25 +82,21 @@ public void AddAlignTokensOperations(List list, SyntaxNode private static ImmutableArray FilterToRulesImplementingMethod(ImmutableArray rules, string name) { - return rules.Where(rule => + return rules.WhereAsArray(rule => { var type = GetTypeImplementingMethod(rule, name); if (type == typeof(AbstractFormattingRule)) - { return false; - } if (type == typeof(CompatAbstractFormattingRule)) { type = GetTypeImplementingMethod(rule, name + "Slow"); if (type == typeof(CompatAbstractFormattingRule)) - { return false; - } } return true; - }).ToImmutableArray(); + }); } private static Type? GetTypeImplementingMethod(object obj, string name) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs index e50090dda0738..1bc2f1c870430 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -63,50 +64,47 @@ public int CompareTo(TokenData other) Contract.ThrowIfFalse(this.TokenStream == other.TokenStream); if (this.IndexInStream >= 0 && other.IndexInStream >= 0) - { return this.IndexInStream - other.IndexInStream; - } + + if (this.Token == other.Token) + return 0; var start = this.Token.SpanStart - other.Token.SpanStart; if (start != 0) - { return start; - } var end = this.Token.Span.End - other.Token.Span.End; if (end != 0) - { return end; - } - // this is expansive check. but there is no other way to check. + // We have two different tokens, which are at the same location. This can happen with things like empty/missing + // tokens. In order to give a strict ordering, we need to walk up the tree to find the first common ancestor + // and see which token we hit first in that ancestor. var commonRoot = this.Token.GetCommonRoot(other.Token); - RoslynDebug.Assert(commonRoot != null); + Contract.ThrowIfNull(commonRoot); - var tokens = commonRoot.DescendantTokens(); + // Now, figure out the ancestor of each token parented by the common root. + var thisTokenAncestor = GetAncestorUnderRoot(this.Token, commonRoot); + var otherTokenAncestor = GetAncestorUnderRoot(other.Token, commonRoot); - var index1 = Index(tokens, this.Token); - var index2 = Index(tokens, other.Token); - Contract.ThrowIfFalse(index1 >= 0 && index2 >= 0); - - return index1 - index2; - } + foreach (var child in commonRoot.ChildNodesAndTokens()) + { + if (child == thisTokenAncestor) + return -1; + else if (child == otherTokenAncestor) + return 1; + } - private static int Index(IEnumerable tokens, SyntaxToken token) - { - var index = 0; + throw ExceptionUtilities.Unreachable(); - foreach (var current in tokens) + static SyntaxNodeOrToken GetAncestorUnderRoot(SyntaxNodeOrToken start, SyntaxNode root) { - if (current == token) - { - return index; - } + var current = start; + while (current.Parent != root) + current = current.Parent; - index++; + return current; } - - return -1; } public static bool operator <(TokenData left, TokenData right) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs index 5d39109a8d864..4f62d0df988a8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs @@ -18,16 +18,12 @@ internal abstract class TriviaData { protected const int TokenPairIndexNotNeeded = int.MinValue; - private readonly string _language; - - protected TriviaData(SyntaxFormattingOptions options, string language) + protected TriviaData(LineFormattingOptions options) { Options = options; - _language = language; } - protected SyntaxFormattingOptions Options { get; } - protected string Language => _language; + protected LineFormattingOptions Options { get; } public int LineBreaks { get; protected set; } public int Spaces { get; protected set; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaDataWithList.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaDataWithList.cs index dff9d8703a24c..9cc7dc29fce5e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaDataWithList.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaDataWithList.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Formatting; -internal abstract class TriviaDataWithList(SyntaxFormattingOptions options, string language) : TriviaData(options, language) +internal abstract class TriviaDataWithList(LineFormattingOptions options) : TriviaData(options) { public abstract SyntaxTriviaList GetTriviaList(CancellationToken cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs index 8073cdc2c43eb..0ab7048ad4140 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -53,7 +54,8 @@ public static bool ContainsElasticTrivia(this SuppressOperation operation, Token var endToken = tokenStream.GetTokenData(operation.EndToken); var previousToken = endToken.GetPreviousTokenData(); - return tokenStream.GetTriviaData(startToken, nextToken).TreatAsElastic || tokenStream.GetTriviaData(previousToken, endToken).TreatAsElastic; + return CommonFormattingHelpers.HasAnyWhitespaceElasticTrivia(startToken.Token, nextToken.Token) || + CommonFormattingHelpers.HasAnyWhitespaceElasticTrivia(previousToken.Token, endToken.Token); } public static bool HasAnyWhitespaceElasticTrivia(this SyntaxTriviaList list) @@ -62,9 +64,7 @@ public static bool HasAnyWhitespaceElasticTrivia(this SyntaxTriviaList list) foreach (var trivia in list) { if (trivia.IsElastic()) - { return true; - } } return false; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs index ef25534b4e233..aa3bf201c3742 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; @@ -18,5 +17,5 @@ internal interface ISyntaxFormatting SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); ImmutableArray GetDefaultFormattingRules(); - IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken); + IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs index 33620eea5d158..8af68c34064e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -20,7 +20,7 @@ public virtual AbstractFormattingRule WithOptions(SyntaxFormattingOptions option /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) => nextOperation.Invoke(); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs index 0b5609b7364b2..08e9a2da873c4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -13,7 +14,7 @@ internal abstract class CompatAbstractFormattingRule : AbstractFormattingRule #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member [Obsolete("Do not call this method directly (it will Stack Overflow).", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] - public sealed override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public sealed override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { var nextOperationCopy = nextOperation; AddSuppressOperationsSlow(list, node, ref nextOperationCopy); @@ -68,7 +69,7 @@ public sealed override void AddAlignTokensOperations(List /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperationsSlow(List list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperationsSlow(ArrayBuilder list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) => base.AddSuppressOperations(list, node, in nextOperation); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs index a3e051ab705c8..f0a02813eb808 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -13,7 +14,7 @@ internal readonly struct NextSuppressOperationAction( ImmutableArray formattingRules, int index, SyntaxNode node, - List list) + ArrayBuilder list) { private NextSuppressOperationAction NextAction => new(formattingRules, index + 1, node, list); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs index 0b2d965c14a0f..ea13eaf68014c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs @@ -141,18 +141,6 @@ public static AdjustSpacesOperation CreateAdjustSpacesOperation(int space, Adjus return new AdjustSpacesOperation(space, option); } - /// - /// return SuppressOperation for the node provided by the given formatting rules - /// - internal static IEnumerable GetSuppressOperations(IEnumerable formattingRules, SyntaxNode node, SyntaxFormattingOptions options) - { - var chainedFormattingRules = new ChainedFormattingRules(formattingRules, options); - - var list = new List(); - chainedFormattingRules.AddSuppressOperations(list, node); - return list; - } - /// /// return AnchorIndentationOperation for the node provided by the given formatting rules /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs index 62dcc67daf5cc..aa20832c3047f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs @@ -346,21 +346,15 @@ public static int GetStartPositionOfSpan(SyntaxToken token) public static bool HasAnyWhitespaceElasticTrivia(SyntaxToken previousToken, SyntaxToken currentToken) { - if ((!previousToken.ContainsAnnotations && !currentToken.ContainsAnnotations) || - (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia)) - { + if (!previousToken.ContainsAnnotations && !currentToken.ContainsAnnotations) + return false; + + if (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia) return false; - } return previousToken.TrailingTrivia.HasAnyWhitespaceElasticTrivia() || currentToken.LeadingTrivia.HasAnyWhitespaceElasticTrivia(); } - public static bool IsNull(T t) where T : class - => t == null; - - public static bool IsNotNull(T t) where T : class - => !IsNull(t); - public static TextSpan GetFormattingSpan(SyntaxNode root, TextSpan span) { Contract.ThrowIfNull(root); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs index 74df6ecf17b13..f59a9424eb31b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs @@ -79,7 +79,7 @@ public override async Task RemoveUnnecessaryImportsAsync( #endif var spans = new List(); AddFormattingSpans(newRoot, spans, cancellationToken); - var formattedRoot = Formatter.Format(newRoot, spans, provider, formattingOptions, rules: null, cancellationToken); + var formattedRoot = Formatter.Format(newRoot, spans, provider, formattingOptions, rules: default, cancellationToken); return document.WithSyntaxRoot(formattedRoot); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb index f62b444c2c872..0fd8a219aa27c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb @@ -5,6 +5,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -18,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation _indentStyle = indentStyle End Sub - Public Overrides Sub AddSuppressOperationsSlow(list As List(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(list As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) ' don't suppress anything End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb b/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb index f581265ed4cf0..49c635c13a72f 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb @@ -5,6 +5,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities @@ -36,7 +37,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Return New DefaultOperationProvider(options) End Function - Public Overrides Sub AddSuppressOperationsSlow(operations As List(Of SuppressOperation), node As SyntaxNode, ByRef nextAction As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(operations As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextAction As NextSuppressOperationAction) End Sub Public Overrides Sub AddAnchorIndentationOperationsSlow(operations As List(Of AnchorIndentationOperation), node As SyntaxNode, ByRef nextAction As NextAnchorIndentationOperationAction) diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.AbstractLineBreakTrivia.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.AbstractLineBreakTrivia.vb index d75d120ab0a90..4b543b4e32ee7 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.AbstractLineBreakTrivia.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.AbstractLineBreakTrivia.vb @@ -15,12 +15,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Protected ReadOnly _original As String Protected ReadOnly _newString As String - Public Sub New(options As SyntaxFormattingOptions, + Public Sub New(options As LineFormattingOptions, original As String, lineBreaks As Integer, indentation As Integer, elastic As Boolean) - MyBase.New(options, lineBreaks, indentation, elastic, LanguageNames.VisualBasic) + MyBase.New(options, lineBreaks, indentation, elastic) Me._original = original Me._newString = CreateStringFromState() diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.vb index 7644e750dd40b..71c51db59f694 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.vb @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Private Class ComplexTrivia Inherits AbstractComplexTrivia - Public Sub New(options As SyntaxFormattingOptions, treeInfo As TreeData, token1 As SyntaxToken, token2 As SyntaxToken) + Public Sub New(options As LineFormattingOptions, treeInfo As TreeData, token1 As SyntaxToken, token2 As SyntaxToken) MyBase.New(options, treeInfo, token1, token2) Contract.ThrowIfNull(treeInfo) End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.vb index ba33b5201885f..07fc99fba20f8 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.vb @@ -8,7 +8,7 @@ Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Partial Friend Class TriviaDataFactory - Private Class FormattedComplexTrivia + Private NotInheritable Class FormattedComplexTrivia Inherits TriviaDataWithList Private ReadOnly _formatter As VisualBasicTriviaFormatter @@ -22,7 +22,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting spaces As Integer, originalString As String, cancellationToken As CancellationToken) - MyBase.New(context.Options, LanguageNames.VisualBasic) + MyBase.New(context.Options.LineFormatting) Contract.ThrowIfNull(context) Contract.ThrowIfNull(formattingRules) diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.LineContinuationTrivia.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.LineContinuationTrivia.vb index 83e0d3bf3d7dd..52007299afb51 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.LineContinuationTrivia.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.LineContinuationTrivia.vb @@ -16,7 +16,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Private Class LineContinuationTrivia Inherits AbstractLineBreakTrivia - Public Sub New(options As SyntaxFormattingOptions, + Public Sub New(options As LineFormattingOptions, originalString As String, indentation As Integer) MyBase.New(options, originalString, 1, indentation, False) diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.vb index 2062779935531..4dcd37bdaca7e 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.vb @@ -3,19 +3,18 @@ ' See the LICENSE file in the project root for more information. Imports System.Threading -Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Partial Friend Class TriviaDataFactory - Private Class ModifiedComplexTrivia + Private NotInheritable Class ModifiedComplexTrivia Inherits TriviaDataWithList Private ReadOnly _original As ComplexTrivia - Public Sub New(options As SyntaxFormattingOptions, original As ComplexTrivia, lineBreaks As Integer, space As Integer) - MyBase.New(options, LanguageNames.VisualBasic) + Public Sub New(options As LineFormattingOptions, original As ComplexTrivia, lineBreaks As Integer, space As Integer) + MyBase.New(options) Contract.ThrowIfNull(original) Me._original = original diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.vb index 11bdd60ff1700..3475c8aad1dc2 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/Trivia/TriviaDataFactory.vb @@ -20,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Private ReadOnly _lineContinuations(s_lineContinuationCacheSize) As LineContinuationTrivia - Public Sub New(treeInfo As TreeData, options As SyntaxFormattingOptions) + Public Sub New(treeInfo As TreeData, options As LineFormattingOptions) MyBase.New(treeInfo, options) End Sub @@ -160,7 +160,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting If result.LineBreaks = 0 AndAlso result.Tab > 0 Then ' calculate actual space size from tab Dim spaces = CalculateSpaces(token1, token2) - Return New ModifiedWhitespace(Me.Options, result.LineBreaks, indentation:=spaces, elastic:=result.TreatAsElastic, language:=LanguageNames.VisualBasic) + Return New ModifiedWhitespace(Me.Options, result.LineBreaks, indentation:=spaces, elastic:=result.TreatAsElastic) End If ' check whether we can cache trivia info for current indentation diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb index ab3418217b4c8..4e89985b11d94 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb @@ -2,7 +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 Microsoft.CodeAnalysis.Diagnostics +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.LanguageService @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Public Sub New(node As SyntaxNode, options As SyntaxFormattingOptions, - formattingRules As IEnumerable(Of AbstractFormattingRule), + formattingRules As ImmutableArray(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken) MyBase.New(TreeData.Create(node), @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Friend Overrides ReadOnly Property HeaderFacts As IHeaderFacts = VisualBasicHeaderFacts.Instance Protected Overrides Function CreateTriviaFactory() As AbstractTriviaDataFactory - Return New TriviaDataFactory(Me.TreeData, Me.Options) + Return New TriviaDataFactory(Me.TreeData, Me.Options.LineFormatting) End Function Protected Overrides Function CreateFormattingResult(tokenStream As TokenStream) As AbstractFormattingResult diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicStructuredTriviaFormatEngine.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicStructuredTriviaFormatEngine.vb index ac7baf64c323a..e683b19beacec 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicStructuredTriviaFormatEngine.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicStructuredTriviaFormatEngine.vb @@ -35,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Friend Overrides ReadOnly Property HeaderFacts As IHeaderFacts = VisualBasicHeaderFacts.Instance Protected Overrides Function CreateTriviaFactory() As AbstractTriviaDataFactory - Return New TriviaDataFactory(Me.TreeData, Me.Options) + Return New TriviaDataFactory(Me.TreeData, Me.Options.LineFormatting) End Function Protected Overrides Function CreateFormattingContext(tokenStream As TokenStream, cancellationToken As CancellationToken) As FormattingContext diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb index 9c87a6a33365c..eac504169c6df 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb @@ -4,6 +4,7 @@ Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting @@ -14,7 +15,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Public Sub New() End Sub - Public Overrides Sub AddSuppressOperationsSlow(list As List(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(list As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) nextOperation.Invoke() End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb index f0b7fee2a86b6..598ce18a2d40b 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb @@ -43,7 +43,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Return New AggregatedFormattingResult(node, results, formattingSpans) End Function - Protected Overrides Function Format(root As SyntaxNode, options As SyntaxFormattingOptions, formattingRules As IEnumerable(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken, cancellationToken As CancellationToken) As AbstractFormattingResult + Protected Overrides Function Format(root As SyntaxNode, options As SyntaxFormattingOptions, formattingRules As ImmutableArray(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken, cancellationToken As CancellationToken) As AbstractFormattingResult Return New VisualBasicFormatEngine(root, options, formattingRules, startToken, endToken).Format(cancellationToken) End Function End Class diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb index 12ab6a61cad72..c313960a29eed 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb @@ -9,10 +9,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification - Partial Friend Class VisualBasicSimplificationService - Inherits AbstractSimplificationService(Of ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) - Private Class NodesAndTokensToReduceComputer Inherits VisualBasicSyntaxRewriter @@ -98,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Me._simplifyAllDescendants = Me._simplifyAllDescendants OrElse token.HasAnnotation(Simplifier.Annotation) If Me._simplifyAllDescendants AndAlso Not Me._insideSpeculatedNode AndAlso token.Kind <> SyntaxKind.None Then - Me._nodesAndTokensToReduce.Add(New NodeOrTokenToReduce(token, simplifyAllDescendants:=True, originalNodeOrToken:=token)) + Me._nodesAndTokensToReduce.Add(New NodeOrTokenToReduce(token, SimplifyAllDescendants:=True, OriginalNodeOrToken:=token)) End If If token.ContainsAnnotations OrElse savedSimplifyAllDescendants Then diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb index e4d912a910015..9246a9f11d22e 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb @@ -6,10 +6,10 @@ Imports System.Collections.Immutable Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Internal.Log Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities @@ -17,7 +17,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicSimplificationService - Inherits AbstractSimplificationService(Of ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) + Inherits AbstractSimplificationService(Of CompilationUnitSyntax, ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) Private Shared ReadOnly s_reducers As ImmutableArray(Of AbstractReducer) = ImmutableArray.Create(Of AbstractReducer)( @@ -177,5 +177,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Next End Sub + Protected Overrides Sub AddImportDeclarations(root As CompilationUnitSyntax, importDeclarations As ArrayBuilder(Of SyntaxNode)) + importDeclarations.AddRange(root.Imports) + End Sub 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 index 4148c89ac8972..99b66db0d1bca 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -24,11 +24,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic 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) + 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 @@ -71,7 +76,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree Return If(ReferenceEquals(root, _root) AndAlso options = Me.Options, Me, - New ParsedSyntaxTree(If(ReferenceEquals(root, _root), _lazyText, Nothing), DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, _checksumAlgorithm)) + New ParsedSyntaxTree( + If(ReferenceEquals(root, _root), _lazyText, Nothing), + DirectCast(root, VisualBasicSyntaxNode), + DirectCast(options, VisualBasicParseOptions), + FilePath, + Encoding, + _checksumAlgorithm)) End Function Public Overrides Function WithFilePath(path As String) As SyntaxTree